zoukankan      html  css  js  c++  java
  • 【正睿2019暑假集训】正睿892 蔡老板与宝藏

    题目链接

    设题目给出的排列为(p[1dots n]),每个位置上数字的颜色为(c[1dots n])

    对每个(i) ((1leq ileq n)),从(i)(p[i])连边。根据排列的性质,可以得到若干个简单环。我们的最终目的,是要把这张“图”,变成只有自环。也就是“拆掉”所有长度大于(1)的环。

    考虑一次操作能带来什么。

    • 如果被操作的两个点,在同一个环上,那么这个环被拆成两个环。特别地,如果这两个点相邻,那么入边的那个点,将变成一个自环,其他点保持原顺序,同时两个点交换颜色(别忘了颜色会交换!)。
    • 如果被操作的两个点,不在同一个环上,那么这两个点所在的两个环,将合并成同一个环。

    在开始操作之前,我们把所有的环,按环里所有点是不是同一种颜色,分为“同色环”和“非同色环”两类。

    对于一个非同色环,我们总能用 环长(-1) 次操作,将其拆掉(拆成若干个自环)。方法是,每次对环上相邻的两个点操作,相当于拆掉了其中一个点。用这种方法,先把环,变成所有相邻点都不同色(每个连续同色段,只保留一个点)。然后再按顺序操作一圈,就能把整个环拆掉。

    对于一个同色环,直接拆是拆不掉的。考虑把它和别的环合并,变成一个非同色环。这里“别的环”的选择,就很重要。如果选另一个同色环与之合并(要求两个同色环,“同”的不能是同一种颜色),则相当于一次解决了两个同色环。而如果选择另一个非同色环与之合并(显然是一定能合并的),则只解决了当前这一个同色环。所以,优先让同色环与同色环合并,是比较好的。

    把所有同色环,按颜色分类。每种颜色,对应了若干个环,称环的数量为这种颜色的“出现次数”。因为同色环合并,两个环的颜色必须不同,所以要配对尽可能多的同色环,可以采取如下的贪心策略:每次取出“出现次数”最多的两种颜色,从中各取一个环,将其合并。可以用一个大根堆(( exttt{priority_queue}))来维护这个过程。

    至此,我们可以用最少的操作次数,拆掉一个非同色环。并且可以贪心地,把所有同色环变成非同色环。于是本题圆满解决。时间复杂度(O(nlog n))

    值得一提的是,有且仅有一种情况是无解的:所有点颜色相同,且至少存在一个位置满足(p[i] eq i)

    参考代码:

    //problem:ZR892
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
    template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
    
    const int MAXN=1e5;
    const int INF=1e9;
    int n,a[MAXN+5],pos[MAXN+5],c[MAXN+5];
    bool vis[MAXN+5];
    vector<int>col_circles[MAXN+5];
    vector<pii>ans;
    inline void work(int i,int j){
    	ans.pb(mk(i,j));
    	swap(a[i],a[j]);
    	pos[a[i]]=i;
    	pos[a[j]]=j;
    	swap(c[i],c[j]);
    }
    void solve(int st){
    	//if(a[st] == st)return;
    	assert(a[st] != st);
    	vis[st]=1;
    	int num = (c[a[st]] != c[st]);
    	for(int i=a[st];i!=st;i=a[i]){
    		vis[i]=1;
    		num += (c[a[i]] != c[i]);
    	}
    	assert(num>0);
    	int p=st;
    	while(c[pos[p]] == c[p])
    		p=pos[p];
    	
    	for(int i=1;i<=num;++i){
    		while(c[pos[p]] == c[pos[pos[p]]]){
    			int nxt=pos[p];
    			work(pos[p],p);
    			p=nxt;
    		}
    		p=pos[p];
    	}
    	for(int i=1;i<num;++i)
    		work(p,a[p]);
    }
    int main() {
    	int t;
    	cin>>n>>t;
    	for(int i=1;i<=n;++i){
    		cin>>a[i];
    		pos[a[i]]=i;
    	}
    	int max_c=-INF,min_c=INF;
    	for(int i=1;i<=n;++i){
    		cin>>c[i];
    		ckmin(min_c,c[i]);
    		ckmax(max_c,c[i]);
    	}
    	if(min_c == max_c){
    		// 只有一种颜色
    		bool fail=false;
    		for(int i=1;i<=n;++i)
    			if(a[i] != i){
    				fail=true;
    				break;
    			}
    		if(fail)
    			cout<<-1<<endl;
    		else
    			cout<<0<<endl;
    		return 0;
    	}
    	for(int i=1;i<=n;++i){ // 找环
    		if(vis[i] || a[i]==i)
    			continue;
    		vis[i]=1;
    		bool onecol=1;
    		for(int j=a[i];j!=i;j=a[j]){
    			vis[j]=1;
    			onecol &= (c[j] == c[i]);
    		}
    		if(onecol){
    			col_circles[c[i]].pb(i);
    		}
    	}
    	priority_queue<pii>que;
    	for(int i=1;i<=n;++i) // 枚举每一种颜色
    		if(SZ(col_circles[i])){
    			que.push(mk(SZ(col_circles[i]),i));
    		}
    	
    	int lst=0;
    	if(SZ(que) == 1){
    		int cc=que.top().se;
    		for(int i=1;i<=n;++i)
    			if(c[i] != cc){
    				lst=i;
    				break;
    			}
    		assert(lst != 0);
    	}
    	
    	while(!que.empty()){
    		int c1 = que.top().se; que.pop();
    		if(!SZ(que)){
    			// c1是最后一个颜色
    			// 只能和非同色环lst消
    			while(SZ(col_circles[c1])){
    				int p=col_circles[c1].back();
    				work(p,lst);
    				lst=p;
    				col_circles[c1].pop_back();
    			}
    			break;
    		}
    		int c2 = que.top().se; que.pop();
    		int p1 = col_circles[c1].back();
    		int p2 = col_circles[c2].back();
    		col_circles[c1].pop_back();
    		col_circles[c2].pop_back();
    		work(p1,p2);//两个颜色不同的"同色环",合并成了一个非同色环
    		lst=p1;
    		if(SZ(col_circles[c1]))
    			que.push(mk(SZ(col_circles[c1]),c1));
    		if(SZ(col_circles[c2]))
    			que.push(mk(SZ(col_circles[c2]),c2));
    	}
    	for(int i=1;i<=n;++i)
    		vis[i]=0;
    	for(int i=1;i<=n;++i){
    		if(vis[i] || a[i] == i)
    			continue;
    		solve(i);//把一个非同色环i消掉(拆成若干个自环)
    	}
    	cout<<SZ(ans)<<endl;
    	for(int i=0;i<SZ(ans);++i)
    		cout<<ans[i].fi<<" "<<ans[i].se<<endl;
    	return 0;
    }
    
  • 相关阅读:
    perl 监控网站域名劫持
    OpenLayers访问Geoserver发布的地图
    基于OpenLayers的地图封装Javascript类定义
    Linux_正则表达式
    Linux_正则表达式
    帆软出品: 7点搞定制药企业数据分析系统开发需求
    帆软出品: 7点搞定制药企业数据分析系统开发需求
    Geoserver地图样式SLD资料收集
    GeoServer地图开发解决方案:地图数据处理篇
    Geoserver发布shapfile,中文字段乱码问题
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13301168.html
Copyright © 2011-2022 走看看