CF1750F Majority
看到这个题目觉得非常神奇。
首先我们考虑设\(g_i\)表示\(i\)长度的答案,但是显然不好转移。
考虑容斥,用总方案数减去不能消成一个的方案数,这里的总方案数要求两端都是黑的,也就是\(2^{i-2}\)。
考虑一个不能弄成全\(1\)的最终状态长什么样,应该是\(2n+1\)段,奇数段为黑,偶数段为白,且每个白段的长度都大于两侧的黑段长度之和。
为此可以设计一个dp:设\(f_{i,j,0}\)表示前面总共有\(i\)个,最后以黑段结尾,且黑段长度为\(j\)的方案数。\(f_{i,j,1}\)表示前面总共有\(i\)个,最后以白段结尾,且白段减前面的黑段长度为\(j\)的方案数。
可以发现转移的形式是前缀和,可以优化到\(O(n^2)\)
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e3+5,M=100+5,K=(1<<10)+5,mod=1e9+7,Mod=mod-1;ll INF=1e18+7;const db eps=1e-5;mt19937 rnd(time(0));
int n,p;ll f[N][N][2],Po[N],g[N],Q[N][N][2];
int main(){
freopen("1.in","r",stdin);
int i,j,h;scanf("%d%d",&n,&p);for(Po[0]=i=1;i<=n;i++) Po[i]=Po[i-1]*2%p;
for(i=1;i<=n;i++){g[i]=(i^1?Po[i-2]:1);
for(j=1;j<i;j++){
//for(h=j+1;h<=i-j;h++) f[i][j][0]=(f[i-j][h][1]*g[j]+f[i][j][0])%p;
f[i][j][0]=g[j]*Q[i-j][j+1][1]%p;
}
for(j=1;j<i;j++){
//for(h=j+1;h<=i;h++) f[i][j][1]=(f[i-h][h-j][0]+f[i][j][1])%p;
f[i][j][1]=Q[i-(j+1)][1][0];
}
for(j=0;j<=i;j++) g[i]-=f[i][j][0];for(j=1;i-j>j;j++) f[i][i-2*j][1]+=g[j];g[i]=(g[i]%p+p)%p;
for(j=i;j;j--) Q[i][j][1]=(Q[i][j+1][1]+f[i][j][1])%p,Q[i][j][0]=(Q[i-1][j+1][0]+f[i][j][0])%p;
}printf("%lld\n",g[n]);
}