• Codeforces 1250E The Coronation


    解题思路

    用2-SAT的思路将题目转化为:已知(n)个二元组(<x,y>),可以算出有多少属于不同二元组的元素((a,b))存在冲突,要在每个二元组(<x,y>)中选择选择一个元素,且要尽可能的少选(y),问是否可以选取(n)个两两不相互矛盾的元素,若可以输出选取方案。

    经过简单的推导可以得到,对于(<x_i,y_i>)(<x_j,y_j>)

    • (x_ix_j)(不)冲突,则(y_iy_j)(不)冲突
    • (x_iy_j)(不)冲突,则(y_ix_j)(不)冲突

    继续用2-SAT的思路,并结合上面的性质:

    • (x_i)仅和(x_j)冲突,则从(x_i)(y_j)连一条边,从(x_j)(y_i)连一条边;
    • (x_i)仅和(y_j)冲突,则从(x_i)(x_j)连一条边,从(y_i)(y_j)连一条边;
    • 若都不冲突则不连边;
    • 若都冲突则无解。

    这样一来,我们得到了一个或多个联通块,根据之前推导的性质,对于第(i)个二元组,如果(i)的选取方法确定了,那么相同联通块里的选取方法也就都确定了,再加上不同联通块之间是互不影响的,我们只需要对每个联通块贪心的选取(y)少的方案,然后把所有联通块的答案加起来就是最终的答案了。

    跑Tarjan缩点或者并查集缩点维护一下就没了。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=110;
    int n,m,k;
    ll a[55],b[55]; char s[55];
    
    int fa[N],sz[N]; bool vis[N];
    int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
    void merge(int x,int y){
    	x=find(x); y=find(y);
    	if(x!=y){sz[y]+=sz[x];fa[x]=y;}	
    }
    
    inline int getsame(ll x){
    	int cnt=0;
    	while(x){cnt++;x-=x&(-x);}
    	return m-cnt;	
    }
    
    void solve(){
    	for(int i=1;i<=n;i++){
    		for(int j=i+1;j<=n;j++){
    			bool c1=false,c2=false;
    			if(getsame(a[i]^a[j])>=k)c1=true;
    			if(getsame(a[i]^b[j])>=k)c2=true;
    			if(c1 && c2)continue;
    			if(!c1 && !c2){printf("-1
    ");return;}
    			if(c2)merge(i,j+n),merge(i+n,j);
    			if(c1)merge(i,j),merge(i+n,j+n);
    		}
    	}
    	vector<int>ans;
    	for(int i=1;i<=n;i++){
    		int fi=find(i),fin=find(i+n);
    		if(fi==fin){printf("-1
    ");return;}
    		if(vis[fi])continue;
    		if(vis[fin]){ans.push_back(i);continue;}
    		if(sz[fi]>sz[fin]){vis[fin]=true;ans.push_back(i);}
    		else vis[fi]=true;
    	}
    	printf("%d
    ",(int)ans.size());
    	for(int i:ans)printf("%d ",i); puts("");
    }
    
    int main()
    {	
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d %d %d",&n,&m,&k);
    		for(int i=1;i<=2*n;i++)fa[i]=i, sz[i]=(i>n?1:0), vis[i]=false;	
    		for(int i=1;i<=n;i++){
    			scanf("%s",s); a[i]=0; b[i]=0;
    			for(int j=0;s[j];j++)a[i]*=2, a[i]+=s[j]-'0';
    			reverse(s,s+m);
    			for(int j=0;s[j];j++)b[i]*=2, b[i]+=s[j]-'0';
    		}
    		solve();
    	}
    	return 0;	
    }
    
  • 相关阅读:
    Qt之QLabel
    在Servlet中使用spring注入的bean
    Matlab中图片保存的5种方法
    LATEX中优化问题如何排列Max——s.t.格式
    Latex 初学者入门(四)-- 多个作者共享同一个地址
    一份不太简短的LaTeX教程 lshort – A short in­tro­duc­tion to LATEX 2elshort – A short in­tro­duc­tion to LATEX 2e
    LaTeX技巧:LaTeX括号总结
    Bibtex使用方法
    Latex初学者入门(三)-- 用BibTeX生成参考文献
    LaTeX之参考文献的写法
  • 原文地址:https://www.cnblogs.com/zengzk/p/11774349.html
Copyright © 2020-2023  润新知