Topcoder Single Round Match 511 Round 1 - Division I, Level Three GameOfLifeDivOne 题解

89 篇文章 1 订阅

题目链接
首先这题是一个dp。
这种变化有一个非常显然的规律,如果有连续的两个颜色相同,那么往上这两个的颜色都不会变化。
所以我们可以将连续 ≥ 2 \geq 2 2的相同的颜色的快看作一个断点。
然后设 d p i , c o l , l dp_{i,col,l} dpi,col,l表示有一个断点在 i i i这个断点的颜色为 c o l col col,黑格子的快熟为l的方案数。
i i i只是这个断点的右端点,我们需要枚举左端点j( j < i j<i j<i),和前一个断点的结尾 k k k,然后转移时还要加上 ( k , j ) (k,j) (k,j)的贡献。
由于时环上,我们需要枚举环上断开的点(第一个断点的开始位置)
时间复杂度 ( n 5 + n 2 ∗ l ) (n^5+n^2*l) (n5+n2l)其实可以优化成 O ( n 5 ) O(n^5) O(n5)的。

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=105;
LL dp[MAXN][2][MAXN];
bool check[MAXN][MAXN][2][2];
int have[MAXN][MAXN][2][2];
int a[MAXN];
int t;
int vv[MAXN],v[3];
inline int calc(int l,int r,int c1,int c2){
	if(l==r-1){
		return 0;
	}
	a[l]=c1;
	a[r]=c2;
	int is=c1^1;
	rb(i,l,r){
		is^=1;
		a[i]=is;
	}
	rb(i,1,t){
		rb(j,l+1,r-1){
			v[0]=(a[j-1]);
			v[1]=(a[j+1]);
			v[2]=(a[j]);
			if(v[1]==v[2]||v[1]==v[0]){
				vv[j]=v[1];
			}
			else{
				vv[j]=v[0];
			}
		}
		rb(j,l+1,r-1){
			a[j]=vv[j];
		} 
	}
	int cnt=0;
	rb(j,l+1,r-1)
		cnt+=a[j];
	return cnt; 
}
bool exi[MAXN][MAXN][2];
class GameOfLifeDivOne{
	public:
	LL theCount(string s, int T, int L){
		memset(check,0,sizeof(check));
		t=T;
		int n=s.length();
		n<<=1;
		rep(i,n)
			s+=s[i];
		s='#'+s;
		rb(l,1,n)
			rb(r,l,n)
				rb(i,l,r)
					if(s[i]!='?')
					exi[l][r][s[i]-'0']=1;
		rb(l,1,n)
			rb(r,l,n){
				rep(c1,2){
					rep(c2,2){
						bool need=c1^1;
						check[l][r][c1][c2]=1;
						rb(k,l,r){
							need^=1;
							if((s[k]-'0')==need||(s[k]=='?')){
								continue;
							}
							check[l][r][c1][c2]=0;
						}
						if(need!=c2) check[l][r][c1][c2]=0;
					}
				}
			}
		rb(l,1,n)
			rb(r,l,n)
				rep(c1,2)
					rep(c2,2){
						if(check[l][r][c1][c2]){
							have[l][r][c1][c2]=calc(l,r,c1,c2);
						}
					}
		LL rest=0;
		n>>=1;
		if(check[1][n][0][1]&&calc(0,n+1,1,0)>=L){
			rest++;
		}	
		if(check[1][n][1][0]&&calc(0,n+1,0,1)>=L){
			rest++;
		}
		if(!exi[1][n][0]){
			rest+=1;
		}
		if(!exi[1][n][1]&&!L){
			rest+=1;
		}
		rb(bound,1,n){//破环成链的位置 
			rep(fir_col,2){
				memset(dp,0,sizeof(dp));
				rb(i,2,n){
					if(!exi[bound][bound+i-1][!fir_col]){
						dp[i][fir_col][(fir_col? i:0)]=1;
					}
				}
				rb(i,2,n){
					rb(wtf,0,n)
						rep(col,2){
							rb(j,1,i-1){
								if(j+bound-1>n) break;
								if(exi[j+bound-1][i+bound-1][col^1]) continue; 
								rb(k,1,j-1){
									rep(col_pre,2){
										if(k==j-1&&col_pre==col) continue;
										if(!check[k+bound-1][j+bound-1][col_pre][col]) continue;
										int tmp=wtf-(col? i-j+1:0);
										tmp-=have[k+bound-1][j+bound-1][col_pre][col];
										if(tmp>=0){
											dp[i][col][wtf]+=dp[k][col_pre][tmp];
										}
									}			
								}
							}
						}
				}
				rb(i,2,n){
					rep(col,2){
						if(check[i+bound-1][n+bound][col][fir_col]){
							int hav=have[i+bound-1][n+bound][col][fir_col];
							rb(wtf,0,n){
								if(hav+wtf>=L){
									rest+=dp[i][col][wtf];
								}
							}
						}
					}
				}
			}
	//		cout<<"#"<<bound<<" "<<rest<<endl;
		}
		return rest;
	}
}solver;
//int main(){
//	cout<<solver.theCount("?????????1???0???1??1??????0??????0??0????1???????",
//12,
//40);
//}
/*
6 5 0
1?0?1?
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值