码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】多边形染色

时间:2020-05-03 12:27:00      阅读:51      评论:0      收藏:0      [点我收藏+]

标签:pre   problem   cpp   return   isl   难点   big   prepare   signed   

Link

蒟蒻做的为数不多的环形\(dp\),技巧不到位,写题解来总结一下。

\(\text{Solution:}\)

\(dp\)柿子还是很好推出来的:\(dp[i][j]\)表示地\(i\)个点染色是\(j\)的方案数。先考虑没有限制的转移:

\[dp[i][j]=\sum_{k\not=j} dp[i-1][k] \]

最后注意一下\(1,n\)的限制。

这题多了三个操作,分别是:限制相邻两个颜色一样,限制固定颜色,以及限制不能填的颜色。

那么我们分类讨论一下,对于有相邻颜色一样的点,就可以从上一步的相同颜色转移而来,同时注意一下是不是有其他两种操作即可。

对于没有相邻颜色限制的点,考虑其他限制,有固定颜色的枚举上一个所有不同于固定颜色的颜色转移;有不能填的颜色的枚举上一个所有颜色特判一下就行。

这题的主要难点在于环形后效性的处理。对于我这种没怎么写过环形\(dp\)的人,还是有点生疏。

对于这个题,瞪眼法看出最后一个颜色与第一个颜色是有关联的。那么我们可以钦定第一个点的颜色,进行\(c\)\(dp\)就可以方便地处理后效性啦。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=987654321;
int n,m,c,ans;
int col[50010],dp[50010][11];
//dp[i]表示i这个点是p颜色,1-i一共的方案数
int vis[50010],sum[50010];
int dislike[50010][20];
signed main() {
	scanf("%lld%lld%lld",&n,&m,&c);
	for(int i=1; i<=m; ++i) {
		int opt,x,y;
		scanf("%lld%lld%lld",&opt,&x,&y);
		if(opt==1)for(int i=1;i<=c;++i){if(i!=y)dislike[x][i]=1;}
		if(opt==2)dislike[x][y]=1;
		if(opt==3) {
			if(y>x)swap(x,y);
			vis[x]=y;
		}
	}
	//vis[big]=small
	/*if(col[1]==0){
		for(int i=1;i<=c;++i)dp[1][i]=1;
	}
	if(col[1]!=0){
		if(col[1]>0)dp[1][col[1]]=1;
		else{
			for(int i=1;i<=c;++i){
				if(i==-col[1])continue;
				dp[1][i]=1;
			}
		}
	}
	for(int i=1;i<=n;++i){
		if(!vis[i])continue;
		if(col[i]<=0)continue;
		col[vis[i]]=col[i];
	}
	for(int i=1;i<=c;++i)sum[1]+=dp[1][i];
	for(int i=2;i<=n;++i){
		if(col[i]==0&&!vis[i]){
			//无限制
			for(int j=1;j<=c;++j){
				dp[i][j]*=(sum[i-1]-dp[i-1][j]);
			}
		}
		else if(col[i]==0&&vis[i]){

		}
	}*/
	/*if(col[1]==0)for(int i=1;i<=c;++i)dp[1][i]=1;
	else if(col[1]>0)dp[1][col[1]]=1;
	else for(int i=1;i<=c;++i){if(i!=-col[1])dp[1][i]=1;}
	for(int i=2;i<=n;++i){
		if(vis[i]){
			if(col[i]==0){
				for(int j=1;j<=c;++j)dp[i][j]=dp[i-1][j];
			}
			else{
				if(col[i]<0){
					for(int j=1;j<=c;++j){
						if(j==-col[i])continue;
						dp[i][j]=dp[i-1][j];
					}
				}
				else{
					dp[i][col[i]]=dp[i-1][col[i]];
				}
			}
		}
		else{
			//not same&&col is free
			if(col[i]==0){
				for(int j=1;j<=c;++j){
					for(int k=1;k<=c;++k){
						if(j==k)continue;
						dp[i][j]+=dp[i-1][k];
					}
				}
				//cout<<"qwq\n";
			}
			else if(col[i]<0){
				for(int j=1;j<=c;++j){
					if(j==-col[i])continue;
					for(int k=1;k<=c;++k){
						if(j==k)continue;
						dp[i][j]+=dp[i-1][k];
					}
				}
			}
			else{
				for(int j=1;j<=c;++j){
					if(j==col[i])continue;
					dp[i][col[i]]+=dp[i-1][j];
				}
			}
		}
	}*/
	for(int p=1; p<=c; ++p) {
		//one‘s color
		if(-col[1]==p)continue;
		for(int i=1; i<=n; ++i)for(int j=1; j<=c; ++j)dp[i][j]=0;//prepare for
		dp[1][p]=1;//first
		for(int i=2; i<n; ++i) {
			
			for(int j=1; j<=c; ++j) {
				if(dislike[i][j])continue;
				if(vis[i]) {
					dp[i][j]+=dp[i-1][j];
					dp[i][j]%=mod;
				} else {
					for(int k=1; k<=c; ++k) {
						if(k==j)continue;
						dp[i][j]+=dp[i-1][k];
						dp[i][j]%=mod;
					}
				}
			}
		}
			for(int i=1; i<=c; ++i) {
				if(dislike[n][i]||i==p)continue;
				if(vis[n])dp[n][i]+=dp[n-1][i],dp[n][i]%=mod;
				else {
					for(int j=1; j<=c; ++j) {
						if(i==j)continue;
						dp[n][i]+=dp[n-1][j];
						dp[n][i]%=mod;
					}
				}
			}
		for(int i=1; i<=c; ++i)ans+=dp[n][i],ans%=mod;
	}
	printf("%lld\n",ans%mod);
	return 0;
}

注意题目细节,操作\(1,2\)并不一定只有一个,所以要开一个数组来存储所有不能染的颜色,就是代码中的\(dislike[][].\)

看代码中思路不清晰的后果,注释了一大堆,写了一百五十多行……

【题解】多边形染色

标签:pre   problem   cpp   return   isl   难点   big   prepare   signed   

原文地址:https://www.cnblogs.com/h-lka/p/12821358.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!