【CF1750F】Majority(容斥+DP)
- 规定一个 \(01\) 串 \(s\) 是好的,当且仅当可以经过若干次下面的操作将它变成全 \(1\):选择一对 \(i,j\) 满足 \(s_i=s_j=1\) 且 \(\sum_{k=i}^js_k\ge\frac{j-i+1}2\),将 \(s_{i\sim j}\) 全部修改为 \(1\)。
- 求有多少个长度为 \(n\) 的 \(01\) 串是好的。
- \(1\le n\le5\times10^3\)
考虑最终状态
容易发现无论如何操作一个串,它所能达到的最终状态是一定的。
一个串无法继续操作的充要条件就是相邻两段 \(1\) 的长度之和小于它们之间的 \(0\) 的长度。
所以考虑直接根据这个最终状态来 DP。
容斥+DP
设 \(f_{i,j}\) 表示长度为 \(i\)、满足两端都是 \(1\) 且最后一段 \(1\) 长度为 \(j\) 的最终状态数。
则长度为 \(i\) 的好串数就是 \(f_{i,j}\)。显然可以容斥计算出 \(f_{i,i}=2^{i-2}-\sum_{j=1}^{i-1}f_{i,j}\)(特殊地,\(f_{1,1}=1\))。
对于 \(j < i\) 的 \(f_{i,j}\),设最后一段 \(0\) 的长度为 \(k\),则可以从满足 \(x+j < k\) 的 \(f_{i-j-k,x}\) 转移,变形一下就是 \((i-j-k)+x < i-2j\)。
于是设 \(g_t\) 为 \(i+j\le t\) 的 \(f_{i,j}\) 之和,可知 \(f_{i,j}=g_{i-2j-1}\times f_{j,j}\)。
代码:\(O(n^2)\)
#include<bits/stdc++.h>
#define Cn const
#define CI Cn int&
#define N 5000
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
int n,X,f[N+5][N+5],g[N+5],d[N+5];
int main()
{
int i;for(scanf("%d%d",&n,&X),f[1][1]=1,i=2;i<=n;++i) g[i]=1;//f[1][1]=1
int j,t=0,p=1;for(i=2;i<=n;++i)//容斥+DP
{
for(j=1;j^i;++j) i-2*j>0&&(f[i][j]=1LL*g[i-2*j-1]*f[j][j]%X);//转移j<i的f[i][j]
for(f[i][i]=p,j=1;j^i;++j) Dec(f[i][i],f[i][j]);p=(p<<1)%X;//容斥f[i][i]
for(t=0,j=1;i+j<=n;++j) Inc(t,f[i][j]),Inc(g[i+j],t);//更新g
}
return printf("%d\n",f[n][n]),0;
}
待到再迷茫时回头望,所有脚印会发出光芒