SS241123C. 朱雀湖(lake)

SS241123C. 朱雀湖(lake)

题意

定义 \(f(S,P)\) 表示字符串 \(S\) 最多可以被 \(P\) 匹配次数,满足每次匹配不交。

给定 \(S,P\) 的长度 \(n,m\),和字符集大小 \(k\)\(n \le 10^6, m \le 2000, k \le 10^9\)

要求字符串 \(P\) 每种字符出现次数不超过 \(2\) 次。问对于所有可能的 \(S,P\)\(\sum f(S,P)\)

solution

\(P\) 的性质很特别,吸引我们关注它。发现 \(S\) 中每个可以被 \(P\) 匹配的位置,至多有一个位置对应区间和它对应的区间有交。

这样就容易想到容斥,因此我们先不管匹配不交的限制,求 \(g(S,P)\) 表示 \(S\) 可以被 \(P\) 匹配多少次。算多的之后再考虑减掉。

我们假设有 \(cnt\) 种合法的 \(P\)

对于在 \([l,r]\) 区间匹配了的情况,贡献就是 \(cnt \times k^{n-m}\),表示每个 \(P\) 确定的时候,剩下 \(n-m\) 个位置随便填。有 \(n-m+1\)\(l\),因此总贡献是 \(cnt \times k^{n-m} \times (n-m+1)\)

然后减去算重了的部分。考虑 \(i<j\)\(i,j\) 重叠,画图理解,发现结论:\(P\) 有且仅有一个 boder,且这个 boder 的长度不超过 \(P\) 的一半。

\(P=xyx\),则我们需要减去 \(S\)\(xyxyx\) 匹配的情况。

先计算有多少个 \(x\) 使得 \(P=xyx\),然后贡献就是 \(cnt_{xyx} \times k^{n-3|x|-2|y|}\times (n-3|x|-2|y|+1)\),需要枚举 \(|x|\)\(|y|=m-2|x|\)

然后你发现你多减了 \(xyxyxyx\) 的情况。需要加回来,然后以此类推来容斥,一共要容斥 \(O(\frac{n}{m})\) 次,每次需要枚举 \(|x|\)\(O(m)\) 的,总时间是 \(O(n)\) 的。

现在我们解决如何算合法的 \(P,x,y\) 的个数。设 \(g_{i,j}\) 表示长度为 \(i\),使用了 \(j\) 种数字,有多少合法的串串。那么 \(cnt = \sum_{j=\lceil \frac{m}{2} \rceil}^{m} \binom{k}{j} g_{m,j}\)。转移的时候枚举新加入的元素插几个及插在哪个位置。预处理这个东西时间是 \(O(m^2)\) 的。计算合法的 \(p=xyx\) 的个数,要求 \(y\) 每个元素最多出现 \(2\) 次,\(x\) 中每个元素不能出现超过 \(1\) 次,且 \(x,y\) 中不能有一样的元素。随便预处理一下就好。

总时间复杂度 \(O(n+m^2)\)

code

感觉式子的细节比较多,很难一下子全部想清楚,只能不断拍小样例来找漏洞。但是码量是不大的。可能需要多练。

debug 了很久,没有关心码风了,因此代码比较丑陋。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace Blossom {
	constexpr int N=1e6+7,M=2e3+7,mod=1e9+7;
	int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
	void _add(int &a,int b) { a=add(a,b); }
	int mul(int a,int b) { return 1ll*a*b%mod; }
	void _mul(int &a,int b) { a=mul(a,b); }
	int n,m,k;
	int kmi[N];
	int g[M][M];
	int ans;
	int bik[M];
	int cntm,sum[M];
	int bi[M][M];
	int inv[M];
	int jc[M];
	ll ksm(ll a,ll b=mod-2) {
		ll s=1;
		while(b) {
			if(b&1) s=s*a%mod;
			a=a*a%mod;
			b>>=1;
		}
		return s;
	}
	void main() {
		sf("%d%d%d",&n,&m,&k);
		jc[0]=1;
		rep(i,1,m) jc[i]=mul(jc[i-1],i);
		kmi[0]=1;
		rep(i,1,n) kmi[i]=mul(kmi[i-1],k);
		bik[0]=1;
		rep(i,1,m) bik[i]=mul(mul(bik[i-1],(k-i+1)),inv[i]=ksm(i));
		g[0][0]=1;
		rep(len,0,m-1) rep(j,(len+1)>>1,min(k,len)) {
			_add(g[len+1][j+1],mul(g[len][j],len+1));
			_add(g[len+2][j+1],mul(g[len][j],1ll*(len+2)*(len+1)/2%mod));
		}
		rep(j,(m+1)>>1,m) _add(cntm,mul(g[m][j],bik[j]));
		ans=mul(cntm,mul(n-m+1,kmi[n-m]));
		int op=1;
		rep(i,0,m) {
			bi[i][0]=1;
			rep(j,1,min(k-i,m>>1)) bi[i][j]=mul(bi[i][j-1],mul(k-i-j+1,inv[j]));
		}
		rep(i,1,m>>1) {
			int y=m-(i<<1);
			rep(j,(y+1)>>1,y) _add(sum[i],mul(g[y][j],mul(bik[j],mul(jc[i],bi[j][i]))));
		}
		if(m>1) 
		rep(i,2,n/(m>>1)) {
			op*=-1;
			rep(x,1,m>>1) {
				int y=m-(x<<1);
				if(n-(i+1)*x-i*y>=0) _add(ans,add(mul(op*sum[x],mul(kmi[n-(i+1)*x-i*y],n-(i+1)*x-i*y+1)),mod));
			}
		}
		pf("%d\n",ans);
	}
}
int main() {
	#ifdef LOCAL
	freopen("my.out","w",stdout);
	#else 
	freopen("lake.in","r",stdin);
	freopen("lake.out","w",stdout);
	#endif
	Blossom :: main();
}
posted @ 2024-11-24 21:48  liyixin  阅读(19)  评论(0编辑  收藏  举报