Atcoder agc033 E - Go around a Circle

題面

題意

給出一個環,上面有n個點,將他劃分爲n段弧,現在要求對每段弧進行紅藍染色,要求染色完成後,從每個點開始通過在環上移動,經過的弧構成的字符串都可以成爲給定的字符串,問一共有幾種染色方法.

做法

首先要確定一些結論.
假設給出字符串的第一個字符爲’R’('B’同理).
分兩種情況進行討論:
1.給定字符串中的所有字符都相同
可以發現環上不存在兩個連續的弧且它們的顏色都是B,因此除了所有弧同色的情況外,可以將圓上的弧看作形如RR…RRRB的多段,每段長度至少爲2,這樣題目就轉化爲了將n段弧分成多段,每段長度都大於1,有幾種填法,答案統計方法與下一種情況相同.
2.給定字符串中的所有字符有R也有B
同樣可以發現環上不存在兩個連續的弧且它們的顏色都是B,而且若給定字符串的第一段連續的R的長度爲l,則可以發現環上最長的連續的R弧的長度不超過l+ll+l%22,記mx=l+lmx=l+l%22,若字符串中間(不能是最後)存在一段長度爲l的R且l爲奇數,則可以發現環上最長的連續的R弧的長度不超過ll,mx=min(mx,l)mx=\min(mx,l).
這樣就可以將圓上的弧看作形如RR…RRRB的多段,其中R的長度爲不大於mxmx的奇數,將它與B看作一組,這樣不難發現n爲奇數時無解,題目就轉化爲了將n/2n/2段弧分成多段,每段長度都不大於(mx+1)/2(mx+1)/2的方案數.
爲了方便表達,令n=n/2,mx=(mx+1)/2n=n/2,mx=(mx+1)/2
首先考慮在線段上怎麼做這道題,記dp[i]dp[i]表示i段線段的答案,這樣dp[x]=i=max(0,imx)i1dp[i]dp[x]=\sum_{i=max(0,i-mx)}^{i-1}dp[i],這個顯然可以用前綴和優化,然後考慮如何在環上做這個問題.
我們可以暴力枚舉第一個點所在的那一段的長度爲多少,這樣環上除這一段外剩下的部分就是一條線段,可以用dp解決,然後累加即可得到答案.

代碼

#include<bits/stdc++.h>
#define ll long long
#define N 200100
#define M 1000000007
using namespace std;

ll n,m,mx,ans,dp[N],qz[N];
char str[N];

inline void Add(ll &u,ll v){u=(u+v)%M;}
int main()
{
	ll i,j,t;
	cin>>n>>m;
	scanf("%s",str+1);
	for(i=1;i<=m;i++) if(str[i]!=str[1]) break;
	mx=i-1;
	if(mx==m)
	{
		dp[0]=qz[0]=ans=1;
		for(i=0;i<=n;i++)
		{
			if(i>=2) dp[i]=qz[i-2];
			qz[i]=(qz[i-1]+dp[i])%M;
			if(n-i>=2) Add(ans,dp[i]*(n-i)%M);
		}
		cout<<ans;
		return 0;
	}
	if(n&1)
	{
		puts("0");
		return 0;
	}
	if(mx%2==0) mx++;
	for(t=0;i<=m;i++)
	{
		if(str[i]==str[1]) t++;
		else
		{
			if(t&1) mx=min(mx,t);
			t=0;
		}
	}
	n/=2,mx=(mx+1)/2;
	dp[0]=qz[0]=1;
	for(i=0;i<=n;i++)
	{
		if(i)
		{
			dp[i]=(M+qz[i-1]-(i-mx-1>=0?qz[i-mx-1]:0))%M;
			qz[i]=(qz[i-1]+dp[i])%M;
		}
		if(n-i<=mx) Add(ans,(n-i)*dp[i]*2%M);
	}
	cout<<ans;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章