cf題面spa
求一個由最多26個、最少k個小寫字母構成的,長度爲n的字符串,這個字符串要知足的要求是——當其中字母按照p和q兩個\(1\)~\(n\)的全排列從新排序時,新的字符串是按照升序排好序的(沒要求老字符串排好序)。.net
虛擬賽時其實已經走到了想出正解的路上我在路上了。正解是這樣——對於排列p,將全部p[i]到p[i+1]連邊,對於q也將全部q[i]和q[i+1]連邊,那麼每條邊就表明前面位置的字母要小於等於後面位置的字母,那對於這個圖中的的全部強連通份量,上邊的字母應該都是相同的。因而縮點產生一個DAG,這個DAG前面的字母小於等於後面的字母,因而BFS一遍刪點,刪點的時候給該點賦一個字母,而後字母加一。最後輸出答案就好。code
虛擬賽時想到的是把p和q掃一遍,而後對於p和q中的一個位置上的不一樣數字,那麼這兩個數字表明的新位置之間的字母應該相同,可是這兩個位置之間的其餘數字也產生了其餘一對對位置,而後複雜度就上去了……亂七八糟的各類沒想清楚。排序
#include<queue> #include<cstdio> #include<algorithm> const int MAXN=2e5+5; int n,k; struct Edge{ int nxt,to; }e[MAXN<<2],e2[MAXN<<2]; int head[MAXN],cnt=1,head2[MAXN],cnt2=1; inline void add(int u,int v) { e[cnt]={head[u],v}; head[u]=cnt++; } inline void add2(int u,int v) { e2[cnt2]={head2[u],v}; head2[u]=cnt2++; } int num=0,id[MAXN];//強連通份量數量 int ru[MAXN];//強連通份量的入度 int ind=1,dfn[MAXN],low[MAXN]; int stack[MAXN],top=0; bool instack[MAXN]; void dfs(int u) { dfn[u]=low[u]=ind++; stack[top++]=u; instack[u]=1; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(!dfn[v]) { dfs(v); low[u]=std::min(low[u],low[v]); } else if(instack[v]) low[u]=std::min(low[v],low[u]); } if(dfn[u]==low[u]) { int v; num++; do{ v=stack[--top]; instack[v]=0; id[v]=num; }while(u!=v); } } char ans[MAXN]; void bfs() { std::queue<int> q; for(int u=1;u<=num;u++) if(!ru[u]) q.push(u); char x='a'; while(!q.empty()) { int u=q.front(); ans[u]=x; if(x!='z') x++; q.pop(); for(int i=head2[u];i;i=e2[i].nxt) { int v=e2[i].to; ru[v]--; if(!ru[v]) q.push(v); } } } int main() { // freopen("test.in","r",stdin); scanf("%d%d",&n,&k); for(int x=0;x<2;x++) { int temp; scanf("%d",&temp); for(int i=1,j;i<n;i++) { scanf("%d",&j); add(temp,j); temp=j; } scanf("%d",&temp); for(int i=1,j;i<n;i++) { scanf("%d",&j); add(temp,j); temp=j; } } for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);//6>5>4>3>2>1 if(num<k) { puts("NO"); return 0; } for(int u=1;u<=n;u++) { for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(id[u]!=id[v]) add2(id[u],id[v]),ru[id[v]]++; } } puts("YES"); bfs(); for(int i=1;i<=n;i++) { putchar(ans[id[i]]); } return 0; }