AFO

练习赛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);
	}
}
posted @ 2019-10-12 16:03  ZUTTER☮  阅读(95)  评论(0编辑  收藏  举报