【NOIP模拟】 (11.6) T2 序列操作
2017-11-06 21:06
337 查看
序列操作
题4000
目描述:
一开始有 n 个非负整数 h[i] (1<=i<=n) ,接下来会进行 m 次操作,第 i 次操作给出一个数 c[i] ,要求你选出 c[i] 个大于0的数并将它们减去1。
问最多可以进行多少轮操作后无法操作(即没有 c[i] 个大于0的数)。
输入格式:
第一行两个数表示 n 和 m。
第二行 n 个数描述 h[i]。
第三行 m 个数描述c[i]。
输出格式:
一行表示答案,即最多可以进行多少轮操作后无法操作。
数据范围:
对于10%的是数据满足:1<=n,m<=5。
对于另外20%的数据满足:1<=n<=8;1<=h[i]<=7。
对于50%的数据满足:1<=n,m<=1000。
对于80%的数据满足:1<=n,m<=100000。
对于100%的数据满足:1<=n,m<=1000000 。
解析:
有三种主流做法:平衡树、二分+贪心、权值线段树。
先说题解方法二分+贪心(其实很慢...)。做法就是二分答案,然后用贪心验证。说到这个题的贪心,几位大佬都没法证明它的正确性(读者有兴趣可以试着证一下)。具体的贪心就是取最开始前 c[i] 个大的(不管后来是不是前 c[i] 大),如果不够,那么就往后面取。就是这样,我只能说看出来贪心也是一种DP,有时候你知道这样做是对的,但你就是无法证明它的正确性......
然后说一下权值线段树。其实就是维护整个序列是单调递增的。举个例子:
假设此时c=5,如果直接取前5大的数减去1,那么整个序列就不满足单增,所以对于3有多个,我们就只能取前两个3减1,才能保证序列的单调性。如图:
于是就变成了:
这样整个序列就仍然保持单调性了。具体细节详见代码。
至于平衡树,作为一个NOIP蒟蒻,对于NOI的算法当然就无能为力了,不过还是贴一位大佬的代码(仅供欣赏)。
代码(二分+贪心):
#include <bits/stdc++.h> using namespace std; const int Max=1001000; int n,m; long long h[Max],c[Max]; long long sum[Max],num[Max]; inline int get_int() { int x=0,f=1; char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') {f=-1;c=getchar();} for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline bool comp(const int &a,const int &b) { return a>b; } inline int check(int k) { long long s=0; memset(num,0,sizeof(num)); memset(sum,0,sizeof(sum)); for(int i=1;i<=k;i++) { if(c[i]>n) return 0; num[c[i]]++; s+=c[i]; } for(int i=n;i>=1;i--) sum[i]=num[i]+sum[i+1]; long long ss=0; for(int i=1;i<=n;i++) { ss+=min(sum[i],h[i]); if(h[i]<sum[i]) sum[i+1]+=sum[i]-h[i]; } if(ss==s) return 1; else return 0; } int main() { //freopen("sequence.in","r",stdin); //freopen("sequence.out","w",stdout); n=get_int(); m=get_int(); for(int i=1;i<=n;i++) h[i]=get_int(); for(int i=1;i<=m;i++) c[i]=get_int(); sort(h+1,h+n+1,comp); int l=0,r=m,mid; while(l<=r) { mid=(l+r)>>1; if(check(mid)) l=mid+1; else r=mid-1; } cout<<l-1<<"\n"; return 0; }
代码(权值线段树):
#include <bits/stdc++.h> using namespace std; const int Max=1001000; int n,m; int h[Max]; struct shu{int maxx,minn,num;}; shu tree[Max*4]; inline int get_int() { int x=0,f=1; char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') {f=-1;c=getchar();} for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline void update(int root) { tree[root].maxx=max(tree[root<<1].maxx,tree[root<<1|1].maxx); tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn); } inline int modify(int root,int num) { tree[root].minn-=num; tree[root].maxx-=num; tree[root].num+=num; } inline void pushdown(int root) { if(tree[root].num!=0) { modify(root<<1,tree[root].num); modify(root<<1|1,tree[root].num); tree[root].num=0; } } inline void build(int root,int l,int r) //建树 { if(l==r) { tree[root].minn=tree[root].maxx=h[l]; return; } int mid=(l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); update(root); } inline int Q(int root,int l,int r,int pos) { if(l==r) return tree[root].minn; pushdown(root); int mid=(l+r)>>1; if(pos<=mid) return Q(root<<1,l,mid,pos); else return Q(root<<1|1,mid+1,r,pos); } inline int Find(int root,int l,int r,int num) //寻找 { if(l==r) return l; pushdown(root); int mid=(l+r)>>1; if(tree[root<<1].maxx>=num) return Find(root<<1,l,mid,num); else return Find(root<<1|1,mid+1,r,num); } inline void change(int root,int l,int r,int L,int R) //区间修改 { if(L<=l&&R>=r) { modify(root,1); return; } pushdown(root); int mid=(l+r)>>1; if(L<=mid) change(root<<1,l,mid,L,R); if(R>mid) change(root<<1|1,mid+1,r,L,R); update(root); } int main() { // freopen("sequence.in","r",stdin); //freopen("sequence.out","w",stdout); n=get_int(); m=get_int(); for(register int i=1;i<=n;i++) h[i]=get_int(); sort(h+1,h+n+1); build(1,1,n); for(register int i=1;i<=m;i++) { int c=get_int(),pos=n-c+1,f1,f2; //pos即为第c大的数的位置 int cur=Q(1,1,n,pos); // 寻找pos的值 if(cur==0) //若cur为0,说明已经找不到c个数满足条件,于是答案就是i-1 { cout<<i-1<<"\n"; return 0; } f1=Find(1,1,n,cur); //寻找值为cur的最靠左的点 if(cur!=tree[1].maxx) f2=Find(1,1,n,cur+1);//cur不是最大值说明右侧还有数,则寻找值cur+最靠左的点 else f2=n+1; c-=(n-f2+1); if(f2!=n+1) change(1,1,n,f2,n); //区间修改 change(1,1,n,f1,f1+c-1); //区间修改 } cout<<m<<"\n"; return 0; }
代码(平衡树):
/* created by duzhenyu */ #include<bits/stdc++.h> typedef unsigned int uint; using namespace std; inline int read(){ char ch=getchar();int i=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();} return i*f; } inline uint unit(){ static uint state0=399989; state0^=(state0<<13); state0^=(state0>>17); state0^=(state0<<5); return state0; } const int Maxn=1e6+50; int n,m,a[Maxn]; struct node{ int val,mn,mx,sze,tag; node *lc,*rc; uint pri; inline void upt();inline void add(int t);inline void pushdown(); }Pool[Maxn],*pool=Pool,*null=Pool,*rt=null; typedef pair<node*,node*> pii; inline void node::upt(){ sze=lc->sze+rc->sze+1; mn=val;mx=val; if(lc!=null)mn=min(mn,lc->mn),mx=max(mx,lc->mx); if(rc!=null)mn=min(mn,rc->mn),mx=max(mx,rc->mx); } inline void node::add(int t){ tag+=t; val+=t; mn+=t; mx+=t; } inline void node::pushdown(){ if(!tag)return; if(lc!=null)lc->add(tag); if(rc!=null)rc->add(tag); tag=0; } inline node* newnode(int v){ ++pool; pool->lc=pool->rc=null; pool->mn=v;pool->mx=v;pool->val=v; pool->sze=1;pool->pri=unit(); return pool; } inline node* merge(node *x,node *y){ if(x==null)return y; if(y==null)return x; if(x->pri>y->pri){ x->pushdown(); x->rc=merge(x->rc,y); x->upt(); return x; }else{ y->pushdown(); y->lc=merge(x,y->lc); y->upt(); return y; } } inline pii split_sze(node *now,int sz){ if(now==null)return make_pair(null,null); now->pushdown(); if(now->lc->sze>=sz){ pii tr1=split_sze(now->lc,sz); now->lc=tr1.second;now->upt(); return make_pair(tr1.first,now); }else{ pii tr1=split_sze(now->rc,sz-now->lc->sze-1); now->rc=tr1.first;now->upt(); return make_pair(now,tr1.second); } } inline pii split_val(node *now,int v){ if(now==null)return make_pair(null,null); now->pushdown(); if(now->val<=v){ pii tr1=split_val(now->rc,v); now->rc=tr1.first;now->upt(); return make_pair(now,tr1.second); }else{ pii tr1=split_val(now->lc,v); now->lc=tr1.second;now->upt(); return make_pair(tr1.first,now); } } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++){a[i]=read();} sort(a+1,a+n+1); for(int i=1;i<=n;i++)if(a[i])rt=merge(rt,newnode(a[i])); for(int i=1;i<=m;i++){ int c=read(); if(rt->sze<c){ printf("%d\n",i-1); return 0; }; if(!c)continue; pii tr1=split_sze(rt,rt->sze-c); if(tr1.first==null||tr1.first->mx<tr1.second->mn){ tr1.second->add(-1); rt=merge(tr1.first,tr1.second); if(rt->mn==0)rt=split_val(rt,0).second; }else{ pii tr2=split_val(tr1.second,tr1.second->mn); if(tr2.second!=null)tr2.second->add(-1),c-=tr2.second->sze; pii tr3=split_val(tr1.first,tr1.first->mx-1); tr3.second=merge(tr3.second,tr2.first); pii tr4=split_sze(tr3.second,c); tr4.first->add(-1); rt=merge(tr3.first,merge(tr4.first,merge(tr4.second,tr2.second))); if(rt->mn==0)rt=split_val(rt,0).second; } } printf("%d\n",m); }
相关文章推荐
- NOIP模拟 序列操作【线段树】
- [NOIP2017模拟]序列操作
- [NOIP模拟] 序列操作
- JZOJ5431. 【NOIP2017提高A组集训10.28】序列操作
- NOIP模拟降雷皇&51nod 1376 最长递增子序列的数量
- 【JZOJ 5431】【NOIP2017提高A组集训10.28】序列操作
- jzoj5249 【NOIP2017提高A组模拟8.10】文本编辑器 (序列修改类问题,数据结构)
- jzoj. 3889. 【NOIP2014模拟10.25B组】序列问题
- [NOIP模拟][状压dp][dfs序列][线段树]
- 【JZOJ5431】【NOIP2017提高A组集训10.28】序列操作
- 【NOIP模拟】 (11.2) T2最佳序列
- 模拟一个数字序列压栈出栈操作,输出所有出栈数字序列
- jzoj3518【NOIP2013模拟11.6A组】进化序列
- 【JZOJ4788】【NOIP2016提高A组模拟9.17】序列
- 【NOIP2011模拟9.20】序列
- jzoj. 3518. 【NOIP2013模拟11.6A组】进化序列(evolve)
- [NOIP2017模拟]操作
- JZOJ 5431. 【NOIP2017提高A组集训10.28】序列操作
- PAT (Advanced Level) 1051. Pop Sequence (25) 是否为pop序列,模拟栈的操作