题解:CF1009G Allowed Letters

Union_of_Britain

2024-04-16 13:28:43

Solution

一个 $O(|\Sigma|^2n)$ 的做法。 字典序最小,则从前往后选能选的最小的。 现在压力给到如何判定。容易想到网络流判定,但是看上去每次判定一下复杂度有点爆炸。那么先尝试从后往前构造一组合法解。根据 [CF1168E](https://www.luogu.com.cn/problem/CF1168E) 的想法,考虑根据后面的解来调整。 具体来说,当前 $p_1$ 我想选 $s$,$s$ 却没有了,那么就找到后面的一个填 $a$ 的位置 $p_2$,让它滚(必要的牺牲)。 那么这个 $p_2$ 怎么办?我让另一个位置 $p_3$ 腾位置出来就可以了。如果有解,最后一定会在某个位置 $p_m$ 结束(历史的必然)。 注意到我只关心两个事情:这个路径上的位置可以做这样的替换,并且最后 $p_m$ 变成的字符 $t$ 必须是有余的。那么我把它看成在字符集大小的图上的路径 $P:s\to t$。同时也说明了这样的路径长度大小 $\le |\Sigma|$。 这样的图怎么构造呢?对于每个已经确定为 $c_i$ 的位置 $i$,从 $c_i$ 连向 $u$,并且标记为 $i$ 的转移,如果 $u\neq c_i$ 并且 $u$ 被允许放在 $i$ 位置。两个节点的连边集合,由于涉及插入、删除和找任意一个,可以选用 `unordered_set`。找到这样的路径之后,修改图即可。修改量是单次 $|\Sigma|^2$ 的。 最后构造答案时类似地动态判定即可。需要注意的是后面不能挪用前面的(时代的抉择)。 这样我们就使用更优的复杂度解决了这道题。 很史的考场代码: ```cpp #include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; int cur[maxn],sze[6],n,m,alo[maxn][7],vis[6],can[6]; char s[maxn]; unordered_set<int> d[6][6]; #define VI vector<int> VI CUR; bool dfs(int u,int t){ if(u==t)return 1; vis[u]=1; for(int i=0;i<6;i++){ if(vis[i])continue; if(d[u][i].size()){ CUR.push_back(i); if(dfs(i,t))return 1; CUR.pop_back(); } } return 0; } void DFS(int u){ can[u]=1; for(int i=0;i<6;i++){ if(can[i])continue; if(d[i][u].size())DFS(i); } } VI findpath(int u,int v){ CUR={u}; for(int i=0;i<6;i++)vis[i]=0; if(dfs(u,v))return CUR; return {}; } signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>s+1;n=strlen(s+1); cin>>m; for(int i=1;i<=m;i++){ int k;string s2; cin>>k>>s2; for(auto u:s2)alo[k][u-'a']=1; alo[k][6]=1; } for(int i=1;i<=n;i++){ if(!alo[i][6])for(int j=0;j<6;j++)alo[i][j]=1; } for(int i=1;i<=n;i++)sze[s[i]-'a']++; for(int i=n;i>=1;i--){ bool flg=0; for(int j=0;j<6;j++){ if(!sze[j])continue; if(alo[i][j]){ sze[j]--;cur[i]=j; for(int k=0;k<6;k++)if(alo[i][k]&&j!=k)d[j][k].insert(i); flg=1;break; } VI F; for(int k=0;k<6;k++)can[k]=0; DFS(j); for(int k=0;k<6;k++){ if(j!=k&&can[k]&&alo[i][k]){ F=findpath(k,j); int t[7]; for(int i=0;i<F.size()-1;i++)t[i]=(*d[F[i]][F[i+1]].begin()),sze[F[i]]++,sze[F[i+1]]--; for(int i=0;i<F.size()-1;i++){ cur[t[i]]=F[i+1]; for(int l=0;l<6;l++)if(alo[t[i]][l]&&l!=F[i+1])d[F[i+1]][l].insert(t[i]); for(int l=0;l<6;l++)if(alo[t[i]][l]&&l!=F[i])d[F[i]][l].erase(t[i]); } sze[k]--; cur[i]=k; for(int l=0;l<6;l++)if(alo[i][l]&&l!=k)d[k][l].insert(i); flg=1; break; } } if(flg)break; } if(!flg)return cout<<"Impossible"<<endl,0; } for(int i=1;i<=n;i++){ for(int j=0;j<6;j++)if(alo[i][j]&&j!=cur[i])d[cur[i]][j].erase(i); int res=-1; for(int k=0;k<6;k++)can[k]=0; DFS(cur[i]); for(int j=0;j<cur[i];j++){ if(!alo[i][j]||!can[j])continue; VI F=findpath(j,cur[i]); int t[7]; for(int i=0;i<F.size()-1;i++)t[i]=(*d[F[i]][F[i+1]].begin()); for(int i=0;i<F.size()-1;i++){ cur[t[i]]=F[i+1]; for(int l=0;l<6;l++)if(alo[t[i]][l]&&l!=F[i+1])d[F[i+1]][l].insert(t[i]); for(int l=0;l<6;l++)if(alo[t[i]][l]&&l!=F[i])d[F[i]][l].erase(t[i]); } res=j; break; } if(res==-1)res=cur[i]; cur[i]=res; cout<<(char)(res+'a'); } return 0; } ```