AT4928 [AGC033E] Go around a Circle

https://www.luogu.com.cn/problem/AT4928
NB题,大受震撼

R R R B B B是交换后等价的,假设我们钦定第一位是 R R R

先考虑一种特殊的情况,全都是 R R R, 则每个点左右至少有一条边是 R R R,只需分第一个点前面是否选择来计算, f i b [ n ] + f i b [ n − 2 ] fib[n]+fib[n-2] fib[n]+fib[n2]

考虑有 B B B的情况
B B B会把 R R R分成若干段,考虑每一段的长度
如果是偶数,那么只需要在边界反复横跳即可
如果为奇数,那么最终环上 R R R段的长度为奇数且不能超过这个数

也不能超过第一段 R R R的长度+1

这样我们就得到了一个 m i mi mi表示一段 R R R最长为多少

把每一段 R R R后面拼一个 B B B,然后做一个简单的DP即可

注意旋转算不同的,所以要乘上第一段的长度

code:

#include<bits/stdc++.h>
#define N 400050
#define mod 1000000007
using namespace std;
int f[N], n, m;
char a[N];
void solve() {
     f[0] = 1;
     for(int i = 1; i <= n; i ++) f[i] = (f[i - 1] + f[i - 2]) % mod;
     printf("%lld ", (f[n] + (n > 1? f[n - 2] : 0)) % mod);
}
int main() {
    scanf("%d%d", &n, &m);
    scanf("%s", a + 1);

    int ans = 0;
    for(int i = 1; i <= m; i ++ ) {
        if(a[i] != a[1]) {
            ans = i;
            break;
        }
    }
    if(!ans) { //printf("*");
        solve();
        return 0;
    }

    if(n & 1) {
        printf("0");
        return 0;
    }

    int gs = 0;
    for(int i = ans; i <= m; i ++) {
        if(a[i] != a[1]) {
            if(gs & 1) ans = min(ans, gs);
            gs = 0;
        } else gs ++;
    }
    
    if(ans & 1) ans ++;
    //printf("**%d  ", ans);
    int s = 0;
    f[0] = s = 0; 
    for(int i = 2; i <= n; i += 2) {
        f[i] = (s + (i <= ans) * i) % mod;
        s = (s + f[i]) % mod;
        if(i > ans) s = (s - f[i - ans] + mod) % mod;
    }
   // for(int i = 1; i <= n; i ++) printf("%d ", f[i]); printf("\n");
    printf("%d", f[n]);
    return 0;
}
posted @ 2021-12-07 07:41  lahlah  阅读(48)  评论(0编辑  收藏  举报