Atcoder agc033 E - Go around a Circle

190 篇文章 2 订阅
84 篇文章 2 订阅

题面

题意

给出一个环,上面有n个点,将他划分为n段弧,现在要求对每段弧进行红蓝染色,要求染色完成后,从每个点开始通过在环上移动,经过的弧构成的字符串都可以成为给定的字符串,问一共有几种染色方法.

做法

首先要确定一些结论.
假设给出字符串的第一个字符为’R’('B’同理).
分两种情况进行讨论:
1.给定字符串中的所有字符都相同
可以发现环上不存在两个连续的弧且它们的颜色都是B,因此除了所有弧同色的情况外,可以将圆上的弧看作形如RR…RRRB的多段,每段长度至少为2,这样题目就转化为了将n段弧分成多段,每段长度都大于1,有几种填法,答案统计方法与下一种情况相同.
2.给定字符串中的所有字符有R也有B
同样可以发现环上不存在两个连续的弧且它们的颜色都是B,而且若给定字符串的第一段连续的R的长度为l,则可以发现环上最长的连续的R弧的长度不超过 l + l l+l l+l% 2 2 2,记 m x = l + l mx=l+l mx=l+l% 2 2 2,若字符串中间(不能是最后)存在一段长度为l的R且l为奇数,则可以发现环上最长的连续的R弧的长度不超过 l l l, m x = min ⁡ ( m x , l ) mx=\min(mx,l) mx=min(mx,l).
这样就可以将圆上的弧看作形如RR…RRRB的多段,其中R的长度为不大于 m x mx mx的奇数,将它与B看作一组,这样不难发现n为奇数时无解,题目就转化为了将 n / 2 n/2 n/2段弧分成多段,每段长度都不大于 ( m x + 1 ) / 2 (mx+1)/2 (mx+1)/2的方案数.
为了方便表达,令 n = n / 2 , m x = ( m x + 1 ) / 2 n=n/2,mx=(mx+1)/2 n=n/2,mx=(mx+1)/2
首先考虑在线段上怎么做这道题,记 d p [ i ] dp[i] dp[i]表示i段线段的答案,这样 d p [ x ] = ∑ i = m a x ( 0 , i − m x ) i − 1 d p [ i ] dp[x]=\sum_{i=max(0,i-mx)}^{i-1}dp[i] dp[x]=i=max(0,imx)i1dp[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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值