NOI模拟20191031【咕】【分治+高消】【中模拟】

woj4782~4784

剪刀运送

咕。

走路

这个博客的T3

送分题

送xx分,,

100个点,100个颜色,每个点每次可以选择成为自己或周围任何一个出点的颜色。求一种2n^2以内的,从起始状态到终结状态的方案。

这是道前置结论题。

首先研究无解的情况。

如果我们保证有合法数量的方案,那还有无解的,只能是终结状态的某种颜色,起始状态没有。

接下来考虑怎么转变状态。

拆分问题,从每个点到对应颜色,变成每种颜色的数量先相等。

 

所以我们的操作1本质是:修改一个点的颜色。

我们胡乱地把颜色统一。修改方式只能是将一种少了的颜色,通过某种给多了的颜色。这样少的可以变多,多的可以变少。其它情况都没意义。

那怎么传递呢。抽象出一条链。我们要把起点的颜色修改为终点的颜色。而路上为了保证别的颜色数量不变,只能是交换。

也就是说,我变成你,你变成我,颜色交换,传递过去。只有在起点的时候,直接赋值。

每次交换都是一次状态变化,我们要答案++。记录下来。

 

现在颜色数量一样了,但点色不对应。

我们开始处理,如果发生不对应,我们就要找一个这样的颜色,然后把它传过来。

传的过程中,保证不影响已经弄好的颜色。

所以第二个操作是:在给定范围内,交换颜色。这是第一个操作的弱化版。

因为颜色数量是一样的,所以最后一定能弄到一起。这样我们就做完了。

 

哦对了,图不一定联通,所以要都扫一遍,用一种数据结构把联通块内点记录下来。

差不多了。

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
const int N=103;
int c1[N],c2[N],a[N],b[N],ans[30003][N],g[N][N],n,m,k;
int cnt;int vis[N];int cntq;int v[N];int belong[N];
int q[N];
void dfs1(int u){
	//cout<<u<<" ";
	q[++cntq]=u;vis[u]=1;
	c1[a[u]]++;c2[b[u]]++;
	for(int i=1;i<=n;i++){
		if(i==u)continue;
		if(vis[i]||!g[u][i])continue;
		dfs1(i);
	}
}
int dfs2(int now,int T,int fa){
	if(now==T)return 1;v[now]=1;
	for(int i=1;i<=n;i++){
		if(i==now)continue;
		if(g[now][i]&&!v[i]&&dfs2(i,T,now)){
			cnt++;memcpy(ans[cnt],ans[cnt-1],sizeof(ans[cnt-1]));
			if(fa==-1)ans[cnt][now]=ans[cnt][i];else swap(ans[cnt][now],ans[cnt][i]);
			return 1;
		}
	}return 0;
}
int dfs3(int now,int T,int limit){
	if(now==T)return 1;v[now]=1;
	for(int i=1;i<=n;i++){
		if(i==now)continue;
		if(g[now][i]&&!v[i]&&belong[i]<=limit&&dfs3(i,T,limit)){
			++cnt;memcpy(ans[cnt],ans[cnt-1],sizeof(ans[cnt-1]));
			swap(ans[cnt][now],ans[cnt][i]);return 1;
		}
	}return 0;
}
signed main(){
	n=in;m=in;k=in;
	for(int i=1;i<=n;i++)a[i]=in;for(int i=1;i<=n;i++)b[i]=in;
	for(int i=1;i<=m;i++){int x=in;int y=in;g[x][y]=g[y][x]=1;}
	++cnt;memcpy(ans[cnt],a,sizeof(a));
//	for(int i=1;i<=n;i++)cout<<g[4][i]<<" ";cout<<endl;
	for(int i=1;i<=n;i++){
		if(vis[i])continue;//cout<<"###################"<<endl;
		//cout<<cnt<<"   ";for(int ii=1;ii<=n;ii++)cout<<ans[cnt][ii]<<" ";cout<<endl;
		memset(c1,0,sizeof(c1));memset(c2,0,sizeof(c2));
		vis[i]=1;cntq=0;dfs1(i);//cout<<endl;
		//for(int ii=0;ii<k;ii++)cout<<c1[ii]<<" ";cout<<endl;for(int ii=0;ii<k;ii++)cout<<c2[ii]<<" ";cout<<endl;
		for(int j=0;j<k;j++)if(c2[j]&&!c1[j]){
			cout<<"Impossible";return 0;
		}
		while(1){
			int flag=0;
			for(int j=1;j<=cntq;j++){
				if(c1[ans[cnt][q[j]]]>c2[ans[cnt][q[j]]]){
					flag=1;
					for(int l=1;l<=cntq;l++){
						if(c1[ans[cnt][q[l]]]<c2[ans[cnt][q[l]]]){
							c1[ans[cnt][q[l]]]++;c1[ans[cnt][q[j]]]--;
							memset(v,0,sizeof(v));
							dfs2(q[j],q[l],-1);break;
						}
					}
					break;
				}
			}
			if(!flag)break;
		}
		for(int j=1;j<=cntq;j++){belong[q[j]]=j;}
		for(int j=cntq;j>=1;j--){
			if(ans[cnt][q[j]]!=b[q[j]]){
				for(int l=1;l<j;l++){
					if(ans[cnt][q[l]]==b[q[j]]){
						memset(v,0,sizeof(v));
						dfs3(q[j],q[l],j);break;
					}
				}
			}
		}
	}
	for(int i=1;i<=cnt;i++){
		for(int j=1;j<=n;j++)cout<<ans[i][j]<<" ";cout<<endl;
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值