CF1250E The Coronation (并查集)

做的时候想到拆点,但是没想到维护关系的方法。

这题我们考虑一个字符串分成原串和相反串,用i和i+n表示,我们要做的是维护所有串的相对关系,也就是选了某些串就要选其他串

考虑任意两个字符串i,j之间的关系,如果他们不反转和一个反转都是满足要求的,那么就不用约束关系

如果不反转和反装都不能满足要求,那么就肯定不行输出-1

如果要反转才能满足要求,那么将i,j+n合并和i+n,j合并,这里用并查集,表示选了一个就要选另一个

同理如果不反转才能满足要求,那么连接i,j和i+n,j+n

这样我们就得到了很多连通性集合,对于集合中的点,选一个必须选其他全部

因此我们可以考虑加权后,选择集合中反转串最小的。因为每个集合都是独立的,不可能有一个串在两个不同的集合里面

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=1e6+10;
const int mod=1e9+7;
int p[N],sz[N];
int n,m,k;
string s[N];
int vis[N];
int find(int x){
    if(p[x]!=x){
        p[x]=find(p[x]);
    }
    return p[x];
}
int check(int a,int b){
    int i,j;
    int tmp1=0,tmp2=0;
    int cnt=0;
    for(i=1,j=1;i<=m;i++,j++){
        if(s[a][i]!=s[b][j]){
            cnt++;
        }
    }
    if(cnt<=k)
        tmp1=1;
    cnt=0;
    for(i=1,j=m;i<=m;i++,j--){
        if(s[a][i]!=s[b][j]){
            cnt++;
        }
    }
    if(cnt<=k)
        tmp2=1;
    if(tmp1&&tmp2)
        return 0;
    if(!tmp1&&tmp2)
        return 1;
    if(tmp1&&!tmp2)
        return 2;
    if(!tmp1&&!tmp2)
        return 3;
}
vector<int> ans;
void solve(){
    int i;
    for(i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int tmp=check(i,j);
            if(tmp==0)
                continue;
            if(tmp==1){
                int pa=find(i);
                int pb=find(j+n);
                if(pa!=pb){
                    p[pa]=pb;
                    sz[pb]+=sz[pa];
                }
                pa=find(j);
                pb=find(i+n);
                if(pa!=pb){
                    p[pa]=pb;
                    sz[pb]+=sz[pa];
                }
            }
            else if(tmp==2){
                int pa=find(i);
                int pb=find(j);
                if(pa!=pb){
                    p[pa]=pb;
                    sz[pb]+=sz[pa];
                }
                pa=find(j+n);
                pb=find(i+n);
                if(pa!=pb){
                    p[pa]=pb;
                    sz[pb]+=sz[pa];
                }
            }
            else if(tmp==3){
                cout<<-1<<endl;
                return;
            }
        }
    }
    ans.clear();
    for(i=1;i<=n;i++){
        int pa=find(i),pb=find(i+n);
        if(pa==pb){
            cout<<-1<<endl;
            return ;
        }
        if(vis[pa])
            continue;
        if(vis[pb]){
            ans.push_back(i);
            continue;
        }
        if(sz[pa]>sz[pb]){
            ans.push_back(i);
            vis[pb]=1;
        }
        else{
            vis[pa]=1;
        }
    }
    cout<<(int)ans.size()<<endl;
    for(auto x:ans){
        cout<<x<<" ";
    }
    cout<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        int i;
        cin>>n>>m>>k;
        k=m-k;
        for(i=0;i<=2*n;i++){
            p[i]=i;
            vis[i]=0;
            if(i>n)
                sz[i]=1;
            else
                sz[i]=0;
        }
        for(i=1;i<=n;i++){
            cin>>s[i];
            s[i]=" "+s[i];
        }
        solve();
    }
    return 0;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13767537.html