【题解】CQ大神原创的一道题
2014-02-23 19:55
134 查看
1、刁难
(difficult.pas/cpp)
题目描述
NOIP2013虐江湖考挂了,DAY1爆0,倒数第一,曾经省选校内总分第一的虐江湖终被姜胡虐,但是为了下一届的省选,虐江湖决定出题刁难下学弟们……现有N个数字,你每次需要选定一个区间,要求满足下列条件后这个区间尽可能长:区间左界为大于0的最靠左的数字,且区间最小值大于0。然后将该区间里每一个数减去该区间最小值……经过若干次这样操作后,这些数字都会变成0。你的任务是对于每次这样的操作,输出其左界与右界和当前区间最小值和区间长度,然后将该区间里每一个数减去该区间最小值,最后一行输出区间最小值总和。学弟们,快虐爆丧心病狂的虐江湖,争取你们的省队名额吧!
输入数据
第1行一个正整数N,表示N个数
第2行N个数,0<Ci<2^32-1
输出数据
M行(M≤N),每行4个数Ai,Bi,Ci,Ki表示左界为Ai,右界为Bi,区间最小值为Ci,区间长度为Ki的符合条件的最长区间,最后一行输出区间最小值总和。
输入样例(difficult.in)
5
3 2 4 5 1
输出样例(difficult.out)
1 5 1 5
1 4 1 4
1 1 1 1
3 4 2 2
4 4 1 1
6
数据规模
20%数据N≤1000
100%数据N≤100000
内存限制 64M
时限1S
------------------------------------------------------------------------------------------------------------------------------------
这道题CQ本来是想考二分+线段树:线段树查询找出最左边不为0的点。。二分查找右端点,,然后再区间更新什么的。。我是照着这个思路写的。
不过这题时间卡得太紧了,有8个点标程都是几乎快1秒了才出答案。。。我的线段树貌似常数略大,8个点都是1.2s出答案。。。所以。。。
tsy用了一种神奇的写法用0.2+秒的时间过了时间最长的点- -,学习了一下:
每个节点要维护的信息:不为0的最左最长区间的最小值和左右端点。(懒标记什么的当然还是要有。。。)
然后这个关键的问题就在up上了。。。
up是这样考虑的:如果左子区间有数不为0,可行区间各个信息都从左区间转移。否则从右区间转移。
有一种特殊情况就是左边的最左可行区间和右边的最左可行区间是并在一起的,那么转移时也把它们合并起来(具体看代码吧。。)
还有个关键问题就是update,不能像普通区间更新那样,而是如果有某区间变化时他原本的最优区间最小值减为了0。。那么就继续递归下去,直到最低层。。。(这样回溯的时候up操作就可以维护出新的各个区间的值)。。
如果线段树的根对应的区间的合法区间最小值,就结束。。否则继续操作。。
这种神级线段树我还真不敢在考场上写。。。看来我学的还是太死了
有个坑爹的地方就是:每组数据都刚好会爆int。。。。CQ说这是他专门坑我们的- -
(difficult.pas/cpp)
题目描述
NOIP2013虐江湖考挂了,DAY1爆0,倒数第一,曾经省选校内总分第一的虐江湖终被姜胡虐,但是为了下一届的省选,虐江湖决定出题刁难下学弟们……现有N个数字,你每次需要选定一个区间,要求满足下列条件后这个区间尽可能长:区间左界为大于0的最靠左的数字,且区间最小值大于0。然后将该区间里每一个数减去该区间最小值……经过若干次这样操作后,这些数字都会变成0。你的任务是对于每次这样的操作,输出其左界与右界和当前区间最小值和区间长度,然后将该区间里每一个数减去该区间最小值,最后一行输出区间最小值总和。学弟们,快虐爆丧心病狂的虐江湖,争取你们的省队名额吧!
输入数据
第1行一个正整数N,表示N个数
第2行N个数,0<Ci<2^32-1
输出数据
M行(M≤N),每行4个数Ai,Bi,Ci,Ki表示左界为Ai,右界为Bi,区间最小值为Ci,区间长度为Ki的符合条件的最长区间,最后一行输出区间最小值总和。
输入样例(difficult.in)
5
3 2 4 5 1
输出样例(difficult.out)
1 5 1 5
1 4 1 4
1 1 1 1
3 4 2 2
4 4 1 1
6
数据规模
20%数据N≤1000
100%数据N≤100000
内存限制 64M
时限1S
------------------------------------------------------------------------------------------------------------------------------------
这道题CQ本来是想考二分+线段树:线段树查询找出最左边不为0的点。。二分查找右端点,,然后再区间更新什么的。。我是照着这个思路写的。
不过这题时间卡得太紧了,有8个点标程都是几乎快1秒了才出答案。。。我的线段树貌似常数略大,8个点都是1.2s出答案。。。所以。。。
tsy用了一种神奇的写法用0.2+秒的时间过了时间最长的点- -,学习了一下:
每个节点要维护的信息:不为0的最左最长区间的最小值和左右端点。(懒标记什么的当然还是要有。。。)
然后这个关键的问题就在up上了。。。
up是这样考虑的:如果左子区间有数不为0,可行区间各个信息都从左区间转移。否则从右区间转移。
有一种特殊情况就是左边的最左可行区间和右边的最左可行区间是并在一起的,那么转移时也把它们合并起来(具体看代码吧。。)
还有个关键问题就是update,不能像普通区间更新那样,而是如果有某区间变化时他原本的最优区间最小值减为了0。。那么就继续递归下去,直到最低层。。。(这样回溯的时候up操作就可以维护出新的各个区间的值)。。
如果线段树的根对应的区间的合法区间最小值,就结束。。否则继续操作。。
这种神级线段树我还真不敢在考场上写。。。看来我学的还是太死了
有个坑爹的地方就是:每组数据都刚好会爆int。。。。CQ说这是他专门坑我们的- -
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<iostream> using namespace std; int n; struct T { int l,r; int ld,rd; long long mint; long long add; }tree[100000*3+10]; void up(int id) { if(tree[id<<1].mint) { tree[id].mint=tree[id<<1].mint; tree[id].ld=tree[id<<1].ld; tree[id].rd=tree[id<<1].rd; if(tree[id].rd==tree[id<<1].r&&tree[id<<1|1].mint&&tree[id<<1|1].ld==tree[id<<1|1].l) { tree[id].mint=min(tree[id].mint,tree[id<<1|1].mint); tree[id].rd=tree[id<<1|1].rd; } } else { tree[id].mint=tree[id<<1|1].mint; tree[id].ld=tree[id<<1|1].ld; tree[id].rd=tree[id<<1|1].rd; } } void pushdown(int id) { if(tree[id].add) { long long x=tree[id].add; tree[id].add=0; tree[id<<1].add+=x;tree[id<<1|1].add+=x; tree[id<<1].mint+=x,tree[id<<1|1].mint+=x; } } void build(int id,int l,int r) { tree[id].l=l;tree[id].r=r; tree[id].add=0; if(l==r) { scanf("%I64d",&tree[id].mint); tree[id].ld=tree[id].rd=l; return; } else { int mid=(l+r)>>1; build(id<<1,l,mid); build(id<<1|1,mid+1,r); up(id); } } void update(int id,int l,int r,long long add) { if(tree[id].l==tree[id].r) { tree[id].mint+=add; return; } if(l<=tree[id].l&&tree[id].r<=r&&tree[id].mint>-add) { tree[id].add+=add; tree[id].mint+=add; } else { pushdown(id); int mid=(tree[id].l+tree[id].r)>>1; if(l<=mid)update(id<<1,l,r,add); if(r>mid)update(id<<1|1,l,r,add); up(id); } } int main() { freopen("difficult.in","r",stdin); freopen("difficult.out","w",stdout); scanf("%d",&n); build(1,1,n); long long ans=0; while(tree[1].mint!=0) { long long x=tree[1].mint; ans+=x; printf("%d %d %I64d %d\n",tree[1].ld,tree[1].rd,x,tree[1].rd-tree[1].ld+1); update(1,tree[1].ld,tree[1].rd,-x); } printf("%I64d\n",ans); return 0; }
相关文章推荐
- android,aidl,绑定远程服务,调用远程服务的方法
- 观察者模式简例
- 厄俄斯的女神
- 内存数据库
- Java虚拟机JVM内存分区及代码执行机制
- asp.net dataTable添加列
- 常见错误解决方法
- Cocos2d-x--全民飞机大战弹窗回弹效果
- 机箱里面噪音太大怎么办
- 虚拟化资源链接
- ltib学习笔记
- [C++学习历程]Visual Studio 2010 中文旗舰版 安装
- [C++学习历程]Visual Studio 2010 中文旗舰版 安装
- [C++学习历程]Visual Studio 2010 中文旗舰版 安装
- ISE与modelsim的安装和使用
- xendesktop配置DDC连接vcenter,添加vcenter证书步骤。
- poj 1845 Sumdiv --- 因数分解
- 通过ajax调用WebService服务
- 程序控制选项卡自动切换
- Django1.5 模块