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();
}
本文来自博客园,作者:liyixin,转载请注明原文链接:https://www.cnblogs.com/liyixin0514/p/18564811