标签:pre problem cpp return isl 难点 big prepare signed
蒟蒻做的为数不多的环形\(dp\),技巧不到位,写题解来总结一下。
\(\text{Solution:}\)
\(dp\)柿子还是很好推出来的:\(dp[i][j]\)表示地\(i\)个点染色是\(j\)的方案数。先考虑没有限制的转移:
最后注意一下\(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