省选九省联考T2 IIIDX(线段树)
Posted david--lj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了省选九省联考T2 IIIDX(线段树)相关的知识,希望对你有一定的参考价值。
题目传送门:https://www.luogu.org/problemnew/show/P4364
期中考后记:期中考刚考完,感觉不咋滴,年排第3。我抗压力太差了。。期末得把rank1抢回来。
本来感觉是个瞎贪心的(对每个歌曲的先决的歌曲为父子关系建树,然后每次找尽量大的孩子)。。其实不是。
贪心仅能骗60分。(省选题不会水的)
前面那个贪心只是瞎YY的。YY的贪心有一定几率出错。
为啥会出错,我一会儿讲。
我们正经得想一个贪心。其实问题变为:
for(i=1;i<=n;++i){
找一个难度为p的歌曲并删去,使在i后面的节点能依照没有p歌曲的歌单下不会没有难度足够大的歌曲的情况下,p尽量大,第i个歌曲难度就是p
}
其实这个贪心的思路非常好想(我最开始想的就是这个,后来去打暴力了,就用了第一个)
我们把设选了歌曲p后需要Ap个大于等于p的歌曲(包括p)。那么,我们只要计算出所有Ap,不就爽歪歪了吗?
最开始,我们将歌曲难度降序排序记为A,Ap=p。
写个伪代码
for(i=1;i<=n;++i){
找到最靠右的节点k使Ak-1<siz[i](i的子树大小)
ans[i]=k;
将ak+1.ak+2....an都减去siz[i];
}
for(i=1;i<=n;++i)输出d[ans[i]]
好了还记得最开始讲的贪心为啥会错吗?
因为,我刚才写的伪代码是错的(错的地方和第一个贪心一毛一样)
正确的在此
for(i=1;i<=n;++i){
找到最靠右的节点k使Ak-1<siz[i](i的子树大小)
如果相同的几个歌曲和k的难度一样,那么,将k移到在序列A中最靠左的那个;
ans[i]=k;
将ak+1.ak+2....an都减去siz[i];
}
为啥要那样干?反正没有后效性,而且贪心吗,就要贪得彻底,预留给自己的子树尽量多的节点。
代码:
#include <bits/stdc++.h> using namespace std; const int N=5e5+10; int n,d[N],ans[N],siz[N],fa[N],wxy[N<<2],dr[N<<2],hd[N],pos,lovewxy[N]; double k; bool cmp(int x,int y){return x>y;} bool vis[N]; void pushup(int rt){wxy[rt]=min(wxy[rt<<1],wxy[rt<<1|1]);} void pushdown(int rt){ if(dr[rt]){ dr[rt<<1]+=dr[rt]; dr[rt<<1|1]+=dr[rt]; wxy[rt<<1]+=dr[rt]; wxy[rt<<1|1]+=dr[rt];dr[rt]=0; } } void build(int rt,int l,int r){ if(l==r){ wxy[rt]=l;return; } int mid=l+r>>1; build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);pushup(rt); } void motify(int rt,int l,int r,int ql,int qr,int d){ if(ql<=l && r<=qr){ wxy[rt]+=d;dr[rt]+=d;return; }pushdown(rt); int mid=l+r>>1; if(mid>=ql)motify(rt<<1,l,mid,ql,qr,d); if(mid+1<=qr)motify(rt<<1|1,mid+1,r,ql,qr,d); pushup(rt); } int query(int rt,int l,int r,int lst){ if(l==r)return wxy[rt]>=lst ? l:l+1; pushdown(rt);int mid=l+r>>1; if(wxy[rt<<1|1]<lst)return query(rt<<1|1,mid+1,r,lst); return query(rt<<1,l,mid,lst); } void dfs(int u){ if(u>n)return; int l,r;double tmp=(double)u*k; l=tmp-1; while(l<tmp)++l; tmp=(double)(u+1)*k; r=tmp-1;while(r<tmp)++r; --r;siz[u]=1; for(int i=r;i>=l;--i) if(i<=n) dfs(i),siz[u]+=siz[i],fa[i]=u; } int main(){ scanf("%d%lf",&n,&k); int i;for(i=1;i<=n;++i)scanf("%d",&d[i]);sort(d+1,d+1+n,cmp);d[n+1]=-1; for(i=n;i>0;--i)hd[i]= d[i]==d[i+1] ? pos:++pos; for(i=1;i<=n;++i)lovewxy[hd[i]]=max(lovewxy[hd[i]],i); int p=k+1; while(p>=k) --p; for(i=p;i>0;--i){dfs(i);}build(1,1,n); for(i=1;i<=n;++i){ if(fa[i]&&!vis[fa[i]]){ motify(1,1,n,ans[fa[i]],n,siz[fa[i]]-1);vis[fa[i]]=1; } int tp=query(1,1,n,siz[i]); tp=lovewxy[hd[tp]]; --lovewxy[hd[tp]]; ans[i]=tp;motify(1,1,n,ans[i],n,-siz[i]); } printf("%d",d[ans[1]]); for(i=2;i<=n;++i)printf(" %d",d[ans[i]]); }
以上是关于省选九省联考T2 IIIDX(线段树)的主要内容,如果未能解决你的问题,请参考以下文章