Codeforces Gym 100958 E Cellular Automaton (Makoto rng_58 Soejima contest) 题解
其实"序列中1的数量有限"是一个非常重要的条件。这意味着我们可以找到序列中的第一个1和最后一个1。
考虑这样一件事情:初始时我们把一个长度为的"滑块"放在刚好在第一个1前面的位置(这时滑块的内部全是0),然后每次把滑块向右移动一格(也就是删掉滑块的第一个字符,在末尾再加上一个),直到滑块移动到序列中最后一个1的右边为止。发现题目中的要求等价于:给每一种滑块的状态分配一个0或1的值,对于任意一个序列做出上面这种操作,序列中1的个数必须等于过程中经过的所有滑块状态的值之和。(这是关键观察)
可能的滑块状态有种,如果一个状态可以通过"向右移动一格"的操作变成另一个状态,我们就在这两个状态之间连有向边。给每条边赋一个权值,如果用这条边转移时在原滑块末尾加上的字符是1则把值赋为-1,否则赋为0。这时题目中的要求就转化为了:对于从"0000000..."(全0)走一圈回到"0000000..."的任意一条路径,它上面所有点和边的权值和为0。(仔细想一下)
而如果序列中的1数量无限,我们压根找不到第一个1和最后一个1,也就不能像这样思考了。
考虑怎么给每个点赋值才能满足上面说的条件。令"0000000..."代表的节点为s。首先对于任意节点i,从s走到i的任意一条路径权值和都是相等的,否则就不满足上面的条件(因为这张有向图显然是强连通的,任何一个点都能走回s)。令表示从s走到i的任意一条路径的权值和。发现对于每个到i直接有边的节点j,都相等(为了满足上面的条件),令这个值为val。则。发现以上总结出的所有条件都是不等式的形式(a=b可以转化为),所以可以用差分约束。
由于题目要求的是字典序至少为S的答案,不妨先检查S是否合法。然后从后往前枚举答案最晚可以从哪一位开始与S不同。我们每次要干的事情相当于是:指定有向图中的一些点必须取0或者必须取1,其他的点随意,判断是否有解。只要把上面说的改一下就行了。
令。一共需要做次差分约束,每次复杂度为,总复杂度。
点击查看代码
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define pii pair <int,int> #define fi first #define se second #define mpr make_pair #define pb push_back void fileio() { #ifdef LGS freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif } void termin() { #ifdef LGS std::cout<<"\n\nPROGRAM TERMINATED"; #endif exit(0); } using namespace std; int w,dist[140],cnt[140],inq[140]; string s,ans=""; vector <pii> rg[140],g[140]; queue <int> q; int relax(int x,int vv,int y) { if(dist[x]>dist[y]+vv) { dist[x]=dist[y]+vv; cnt[x]=cnt[y]+1; if(cnt[x]>(1<<(w+w+1))+3) return -1; return 1; } else return 0; } bool spfa() { rep(i,135) dist[i]=1e9,cnt[i]=inq[i]=0; dist[0]=0;inq[0]=1;q.push(0); while(!q.empty()) { int f=q.front();q.pop();inq[f]=0; rep(i,g[f].size()) { int val=relax(g[f][i].fi,g[f][i].se,f); if(val==-1) return false; if(val==1&&inq[g[f][i].fi]==0) q.push(g[f][i].fi),inq[g[f][i].fi]=1; } } return true; } bool check() { rep(i,135) g[i].clear(); rep(i,1<<(w+w+1)) { for(int j=1;j<rg[i].size();++j) { int xa=rg[i][0].fi,xb=rg[i][j].fi,va=rg[i][0].se,vb=rg[i][j].se; g[xa].pb(mpr(xb,vb-va));g[xb].pb(mpr(xa,va-vb)); } if(rg[i].size()) { int xx=rg[i][0].fi,vv=rg[i][0].se,ll=0,rr=1; if(i<ans.size()) { if(ans[i]=='0') rr=0; else ll=1; } g[i].pb(mpr(xx,vv-ll));g[xx].pb(mpr(i,rr-vv)); } } return spfa(); } int main() { fileio(); freopen("cell.in","r",stdin); freopen("cell.out","w",stdout); cin>>w>>s; rep(i,1<<(w+w+1)) { rep(j,2) { int to=(i>>1)|(j<<(w+w)); rg[to].pb(mpr(i,j)); } } ans=s;if(check()){cout<<ans<<endl;termin();} for(int i=s.size()-1;i>=0;--i) if(s[i]=='0') { ans=s.substr(0,i);ans.pb('1'); if(check()) { while(ans.size()<s.size()) { ans.pb('0'); if(!check()) ans[ans.size()-1]='1'; } cout<<ans<<endl; termin(); } } puts("no"); termin(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话