codeforces1213F tarjan缩点+拓扑排序

题意

给定两个长度为n的排列p和q,构造一个字符串s满足\(s[p_i]<=s[p_{i+1}]\)和\(s[q_i]<=s[q_{i+1}]\),且满足字符串中不同字符的个数不少于k。

分析

建一个有向图,\(p_i\)到\(p_{i+1}\)连一条有向边,\(q_i\)到\(q_{i+1}\)连一条有向边。

一条链上的点我们可以贪心的让每个点的字符递增,递增到'z'后,让其余字符全部等于'z',而在同一个环中的所有点的字符一定是相同的,可以把所有环都缩成一个点,那么这张图就变成了有向无环图,可以直接拓扑排序做了。

Code

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define pb push_back
#define ll long long
using namespace std;
const int inf=1e9;
const int mod=1e9+7;
const int maxn=2e5+10;
int n,k;
int low[maxn],dfn[maxn],st[maxn],inst[maxn],top=-1,cnt,scnt,sc[maxn];
vector<int>g[maxn];
set<int>f[maxn];
int d[maxn],rk[maxn];
int res=-1;
void dfs(int u){
    dfn[u]=low[u]=++cnt;
    st[++top]=u;
    inst[u]=1;
    for(int x:g[u]){
        if(!dfn[x]){
            dfs(x);
            low[u]=min(low[u],low[x]);
        }else if(inst[x]){
            low[u]=min(low[u],dfn[x]);
        }
    }
    if(low[u]==dfn[u]){
        scnt++;
        while(true){
            int x=st[top--];
            sc[x]=scnt;
            inst[x]=0;
            if(x==u) break;
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    //freopen("in","r",stdin);
    cin>>n>>k;
    int x,y;
    cin>>x;
    int rt=x;
    for(int i=1;i<n;i++){
        cin>>y;
        g[x].pb(y);
        x=y;
    }
    cin>>x;
    for(int i=1;i<n;i++){
        cin>>y;
        g[x].pb(y);
        x=y;
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) dfs(i);
    }
    for(int i=1;i<=n;i++){
        for(int x:g[i]){
            if(sc[i]!=sc[x]) f[sc[i]].insert(sc[x]);
        }
    }
    for(int i=1;i<=n;i++)
        for(int x:f[i])
            d[x]++;
    queue<int>q;
    int mx=-1;
    for(int i=1;i<=scnt;i++) rk[i]=-1;
    for(int i=1;i<=scnt;i++){
        if(d[i]==0) q.push(i),rk[i]=mx=0;
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int x:f[u]){
            rk[x]=max(rk[u]+1,rk[x]);
            rk[x]=min(25,rk[x]);
            mx=max(mx,rk[x]);
            if(--d[x]==0) q.push(x);
        }
    }
    if(mx+1<k) cout<<"NO\n";
    else{
        cout<<"YES\n";
        for(int i=1;i<=n;i++){
            cout<<(char)(rk[sc[i]]+'a');
        }cout<<'\n';
    }
    return 0;
}
上一篇:一本通tarjan题目


下一篇:UVA1364 Knights of the Round Table Tarjan求点双联通分量+二分图染色