• Atcoder Grand Contest 033


    033E Go around a Circle

    题目描述

    点此看题

    解法

    如果 \(S_1=B\),我们可以翻转整个 \(S\),那么可以让 \(S_1=R\) 并且答案不变。

    那么开始写必要条件,你一定要利用好 满足将棋子放在任意一个点上,都存在方案 这个限制。首先我们可以知道环上不存在相邻的两个 B,因为起点放在 B 之间就会不合法。

    我们可以特判掉 \(S\) 全是 R 的情况,那么现在的 \(S\) 就是 R/B 分段间隔出现,设这些段是 \(c_{1,2...k}\)

    我们发现环上每段 R 都必须是奇数,因为如果是偶数,那么无论 \(c_1\) 是奇数或者偶数,我们都可以通过设置起点,使得走完 \(c_1\) 之后还卡在 R 段的中间。

    此外 \(c_1\) 的要求还没有考虑完,如果 \(c_1\) 是奇数,那么要求每一段的 R 不超过 \(c_1\),因为如果长度超过 \(c_1\),放置在一段无法走到另一端;如果 \(c_1\) 是偶数,那么要求每一段 R 的长度不超过 \(c_1+1\),类似地我们放置在一段的下一个点无法走到另一端。

    对于 \(S\) 中其他的 R 段,起点一定是环上 R 的一段,所以如果对应的 \(c_i\) 是奇数,那么只能从一段走到另一端,所以要求每一段的长度都不能超过 \(c_i\);如果对应的 \(c_i\) 是偶数,只会在端点反复横跳,所以没有限制。总结一下就是对于 \(i\in[2,k)\)(最后一段没有限制),如果 \(i\)\(c_i\) 都是奇数,那么长度不超过 \(c_i\)

    有了上面的必要条件之后充分性不难说明。


    那么现在的限制转化为:求环的方案数,要求不能有相邻的 B,并且 R 的连续段不能超过某个定值。

    直接一维 \(dp\),设 \(f_i\) 表示前 \(i\) 个点分段(一段的 RB 一起考虑)的方案数,注意我们只对偶数位进行 \(dp\),可以用前缀和优化。最后算答案的时候考虑翻转,如果最后一段长度为 \(i\) 就有 \(i\) 种翻转方案:

    \[ans=\sum f_{n-i}\cdot i \]

    时间复杂度 \(O(n)\)

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 200005;
    #define int long long
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,w,ans,c[M],dp[M][2][2],g[M],f[M];char s[M];
    void work()
    {
    	dp[1][0][0]=dp[1][1][1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		dp[i][0][0]=(dp[i-1][0][0]+dp[i-1][0][1])%MOD;
    		dp[i][0][1]=dp[i-1][0][0];
    		dp[i][1][0]=(dp[i-1][1][0]+dp[i-1][1][1])%MOD;
    		dp[i][1][1]=dp[i-1][1][0];
    	}
    	ans=(dp[n][0][0]+dp[n][0][1]+dp[n][1][0])%MOD;
    	printf("%lld\n",ans);
    }
    signed main()
    {
    	n=read();m=read();scanf("%s",s+1);
    	for(int i=1,j=1;i<=m;i=j)
    	{
    		j=i;while(j<=m && s[i]==s[j]) j++;
    		c[++k]=j-i;
    	}
    	if(k==1) {work();return 0;}
    	if(n&1) {puts("0");return 0;}
    	w=c[1]+!(c[1]&1);
    	for(int i=3;i<k;i+=2)
    		if(c[i]&1) w=min(w,c[i]);
    	f[0]=g[0]=1;
    	for(int i=2;i<=n;i+=2)
    	{
    		f[i]=(g[i-2]+(i-w-3>=0?MOD-g[i-w-3]:0))%MOD;
    		g[i]=(g[i-2]+f[i])%MOD;
    	}
    	for(int i=2;i<=min(n,w+1);i+=2)
    		ans=(ans+f[n-i]*i)%MOD;
    	printf("%lld\n",ans);
    }
    
  • 相关阅读:
    如何使用 @ OutputCache 指令的 VaryByCustom 属性来缓存不同版本的页面
    看不懂 ASP.NET 相册上传代码
    asp.net判断是1.1还是2.0主要由Code*属性来解决,判断规则如下:
    引号看不懂
    GridView的行删除事件 //取当前行的Id
    <Columns></Columns>中间的是列集合
    DropDownList1.SelectedIndex = 0 DropDownList1处于位选择任何选项的状态下
    是一个查询语句 查询ZhuanJia表里面id=输入id的数据
    验证码图片的解释
    get和post区别:
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16051816.html
Copyright © 2020-2023  润新知