练习赛28
链接:D
来源:牛客网
题目描述
n个字符串q次询问,每次询问n个字符串去掉\(k_i\)的所有情况剩下的字符串两两间前缀相同最大长度和。
\(N\leq 4000,Q\leq 200,\sum|S|\leq3000000,k\leq300\).
其实每两个字符串的最长前缀在答案中的出现次数一定是一样的,所以答案一定是sum前缀的倍数
考虑答案中一共出现了几个lcp
\[\frac{C_n^k\times C_{n-k}^2}{C_n^2}\times sum
\]
\[=C_{n-2}^k\times sum
\]
wzx神仙一下就想到了第二个柿子ORZ ORZ ORZORZ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define M 1000000007
#define LL long long
#define re register
#define maxn 3000005
#define max(a,b) ((a)>(b) ? (a): (b))
using namespace std;
LL i,m,n,j,k,A[1000001],B[1000001],inv[1000001],z,x,y,q;
LL cnt;
LL son[maxn][26];
char S[maxn];
LL flag[maxn],sum[maxn],deep[maxn];
LL ans=0;
inline void ins()
{
scanf("%s",S+1);
int L=strlen(S+1);
int now=0;
for(re int i=1;i<=L;i++)
{
if(!son[now][S[i]-'a']) son[now][S[i]-'a']=++cnt;
sum[son[now][S[i]-'a']]++;
ans=(ans+(sum[now]-sum[son[now][S[i]-'a']])*(i-1))%M;
now=son[now][S[i]-'a'];
}
ans=(ans+L*(sum[now]-1))%M;
}
int main()
{
scanf("%lld%lld",&n,&q);
inv[1]=A[1]=B[1]=1;
A[0]=B[0]=inv[0]=1;
for(i=2;i<=n;i++)
{
inv[i]=(M-M/i)*inv[M%i]%M;
B[i]=(LL)B[i-1]*(LL)inv[i]%M;
A[i]=(LL)A[i-1]*i%M;
}
for(re LL i=1;i<=n;i++) ins();
for(i=1;i<=q;i++)
{
scanf("%lld",&k);
printf("%lld\n",(LL)A[n-2]%M*B[k]%M*B[n-k-2]%M*ans%M);
}
}