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[n−2]
考虑有
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;
}