洛谷 P7971 [KSN2021] Colouring Balls 题解
纪念一下人生中第一道数据分治题。
Subtask 1&2
这一部分具有的特殊性质是颜色段连续,那我们可以判断当前与前一个的颜色数是否为 1,若是则颜色相同,否则让颜色 +1。
Code
namespace AB { void solve() { int cur=1; ans[1]=1; for(int i=2;i<=n;i++) { if(query(i-1,i)>1) ans[i]=++cur;//颜色不同 else ans[i]=cur; } cout<<"! "; for(int i=1;i<=n;i++) cout<<ans[i]<<' '; cout<<endl; } }
Subtask 3&4
这一部分由于颜色数不多,我们可以记录当前位置已经出现的颜色数量以及每种颜色最后出现的位置,根据这个直接分类讨论当前位置的颜色。
Code
namespace CD { typedef pair<int,int> pii; int last[10],cnt; pii mem[10],fir,sec,thi,fou; void tturn(pii &a,pii &b,pii &ans) { mem[1]=a,mem[2]=b,mem[3]=ans; sort(mem+1,mem+4); a=mem[1],b=mem[2],ans=mem[3]; return; } void fturn(pii &a,pii &b,pii &ans,pii &d) { mem[1]=a,mem[2]=b,mem[3]=ans,mem[4]=d; sort(mem+1,mem+5); a=mem[1],b=mem[2],ans=mem[3],d=mem[4]; return; } void refer(int i,int ncol) { ans[i]=ncol; last[ncol]=i; return; } #define id second void count(int t) { ans[1]=cnt=last[1]=1; if(t==3) { for(int i=2;i<=n;i++) { fir={last[1],1}; sec={last[2],2}; thi={last[3],3}; tturn(fir,sec,thi); if(!sec.first) { if(query(thi.first,i)==2) refer(i,++cnt); else refer(i,1); } else if(!fir.first) { if(query(sec.first,i)==3) refer(i,++cnt); else { if(query(thi.first,i)==1) refer(i,thi.id); else refer(i,sec.id); } } else { if(query(sec.first,i)==3) refer(i,fir.id); else { if(query(thi.first,i)==1) refer(i,thi.id); else refer(i,sec.id); } } } } else { for(int i=2;i<=n;i++) { fir={last[1],1}; sec={last[2],2}; thi={last[3],3},fou={last[4],4}; fturn(fir,sec,thi,fou); if(!sec.first) { if(!thi.first) { if(query(fou.first,i)==2) refer(i,++cnt); else refer(i,1); } else { if(query(thi.first,i)==3) refer(i,++cnt); else { if(query(fou.first,i)==1) refer(i,fou.id); else refer(i,thi.id); } } } else if(!fir.first) { if(query(thi.first,i)==3) { if(query(sec.first,i)==4) refer(i,++cnt); else refer(i,sec.id); } else { if(query(fou.first,i)==1) refer(i,fou.id); else refer(i,thi.id); } } else { if(query(thi.first,i)==3) { if(query(sec.first,i)==4) refer(i,fir.id); else refer(i,sec.id); } else { if(query(fou.first,i)==1) refer(i,fou.id); else refer(i,thi.id); } } } } cout<<"! "; for(int i=1;i<=n;i++) cout<<ans[i]<<' '; cout<<endl; return; } #undef id }
Subtask 5
首先特判掉每个位置颜色都不同的情况。
然后我们考虑双指针,维护两个指针 ,让它们不断向中间移动的同时保证 的颜色数等于 ,最后找到的两个指针的位置就是颜色相同的地方。
Code
namespace E { void solve() { int i=1,j=n,cur=0; if(query(1,n)==n) { cout<<"! "; for(int k=1;k<=n;k++) cout<<k<<' '; cout<<endl; return; } while(query(i,j)==j-i) i++; i--; while(query(i,j)==j-i) j--; j++; for(int k=1;k<=n;k++) { if(k==j) ans[k]=ans[i]; else ans[k]=++cur; } cout<<"! "; for(int k=1;k<=n;k++) cout<<ans[k]<<' '; cout<<endl; return; } }
Subtask 6&7
考虑二分,判断 的颜色数是否等于 的颜色数,如果相同则说明 中有与 颜色相同的点,暴力数出来即可。
Code
namespace FG { int tot,tim,v[10005]; inline int count(int l,int r) { int s=0; ++tim; for(int i=l;i<=r;++i) { if(v[ans[i]]!=tim) { v[ans[i]]=tim; ++s; } } return s; } int main() { int l,r,pos,mid; ans[1]=1; tot=1; for(int i=2;i<=n;++i) { l=1; r=i-1; pos=i; while(l<=r) { mid=(l+r)>>1; if(query(mid,i)!=count(mid,i-1)) { r=mid-1; pos=mid; } else l=mid+1; } if(pos==1) ans[i]=++tot; else ans[i]=ans[pos-1]; } cout<<"! "; for(int i=1;i<=n;i++) cout<<ans[i]<<' '; cout<<endl; return 0; } }
本文作者:Day_Dreamer_D
本文链接:https://www.cnblogs.com/2020gyk080/p/16878561.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步