Description
https://www.luogu.com.cn/problem/CF1750F
Solution
好题!我重开了。
如果一个串合法,每个 $0$ 段一定可以从两边的 $1$ 段转移过来。
设 $f_{i,j}$ 表示长度为 $i$ ,合并到不能再合并以后最后一段 $1$ 的长度为 $j$。那么答案为 $f_{n,n}$。
考虑计算 $f_{i,i}$,容斥有 $f_{i,i}=2^{n-2}=\sum\limits_{j<i}f_{i,j}$。
然后计算 $f_{i,j}$,我们钦定最右边的 $j$ ,然后左边接上一段 $0$ 和 $01$
串使得两者不能合并。
$f_{i,j}=f_{j,j}\sum\limits_{k=j+2}^{i-j-1}\sum\limits_{l=1}^{k-j-1} f_{i-k-j,l}$
然后打表或者通过更高妙的方式,发现后面要加的即为满足 $(i-k-j)+l<i-2j$。前缀和优化即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> #define int long long #define ull unsigned long long #define maxn 5005 #define put() putchar('\n') #define Tp template<typename T> #define Ts template<typename T,typename... Ar> using namespace std; Tp void read(T &x){ int f=1;x=0;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();} x*=f; } namespace Debug{ Tp void _debug(char* f,T t){cerr<<f<<'='<<t<<endl;} Ts void _debug(char* f,T x,Ar... y){while(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);} #define gdb(...) _debug((char*)#__VA_ARGS__,__VA_ARGS__) }using namespace Debug; #define fi first #define se second #define mk make_pair int mod; int power(int x,int y=mod-2) { int sum=1; while (y) { if (y&1) sum=sum*x%mod; x=x*x%mod;y>>=1; } return sum; } int f[maxn][maxn],sum[10005],d[10005]; int n; void add(int &x,int y) {x=(x+y)%mod;} signed main(void){ int i,j,k,l,res=1; read(n);read(mod); f[1][1]=1;d[2]=1; for (i=2;i<=n;i++) { for (j=1;j<=i*2;j++) sum[j]=(sum[j-1]+d[j])%mod; for (j=1;j<i;j++) { if (i>=2*j+1) f[i][j]=(sum[i-2*j-1])*f[j][j]%mod; } f[i][i]=res; for (j=1;j<=(i-1)/2;j++) add(f[i][i],mod-f[i][j]); for (j=1;j<=i;j++) add(d[i+j],f[i][j]); res=res*2%mod; } printf("%lld",f[n][n]); return 0; }
|