【CF1750F Majority】(容斥+dp)

原题链接

题意

定义一个 01 串 \(s\) 是好的,当且仅当 \(s\) 可以通过以下操作变成全是 \(1\) 的串,可以操作无数次。

选择 \(i,j\) 满足 \(i<j,s_i=s_j=1,2\sum_{k=i}^js_k\ge j-i+1\),然后将 \(k\in[i,j]\)\(s_k\) 全部改为 \(1\)

给定 \(n\)\(m\),求有多少个长度为 \(n\)\(01\) 串是好的,对 \(m\) 取模。

\(1 \leq n \leq 5000\)

思路

注意到好的 01 串两端一定均为 \(1\)。考虑设 \(f_{i,j}\) 表示长度为 \(i\) 的 01 串操作到最终状态时,右端点所在的 \(1\) 连续段长度为 \(j\) 的方案数。

初始时,\(f_{1,1}=1\)。最终的答案就是 \(f_{n,n}\)

对于 \(f_{i,i}\),考虑容斥,用总的方案数减去所有的不合法方案数,即:

\[f_{i,i}=2^{i-2}-\sum_{j=1}^{\left \lfloor \frac{i-1}{2} \right \rfloor } f_{i,j} \]

而对于其他状态,中间一定存在一个较长的 \(0\) 连续段,于是可以枚举中间 \(0\) 连续段的长度和右侧 \(1\) 连续段的长度,需要注意边界条件,这里右侧的 \(1\) 连续段不能和左侧的进行操作,即:

\[f_{i,j}=f_{j,j}\sum_{k=j+2}^{i-j-1} \sum_{l=1}^{k-j-1} f_{i-j-k,l} \]

其中的两个限制条件也就是 \(j+k<i,j+l<k\),稍微改写一下可以得到 \(l+(i-j-k)<i-2j\)。那么只需要维护一个 \(s_i\) 表示所有 \(j+k \leq i\)\(f_{j,k}\) 之和即可做到 \(O(n^2)\) 转移。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5010;
int n,m,f[N][N],sum1[N<<1],sum2[N<<1];
void Add(int &a,int b){a+=b;if(a>=m) a-=m;}
void Sub(int &a,int b){a-=b;if(a<0) a+=m;}
int main()
{
	scanf("%d%d",&n,&m);f[1][1]=1;int mul_2=1;sum1[2]=1;
	for(int i=2;i<=n;i++)
	{
		f[i][i]=mul_2;mul_2=mul_2*2%m;
		for(int j=1;j<=n;j++) sum2[j]=(sum2[j-1]+sum1[j])%m;
		for(int j=1;j*2<i;j++)
		{
			Add(f[i][j],1ll*f[j][j]*sum2[i-2*j-1]%m);
			Sub(f[i][i],f[i][j]);
		}
		for(int j=1;j<=i;j++) Add(sum1[i+j],f[i][j]);
	}
	printf("%d\n",f[n][n]);
	return 0;
}
posted @ 2023-03-10 14:31  曙诚  阅读(46)  评论(0)    收藏  举报