牛客练习赛28 D 随风飘(dp + 字符串哈希)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82956275

能用字符串哈希解决的问题,千万别用后缀数组、字典树什么的了……

这题有很多个询问,每次询问是从n个中拿走k个字符串,问拿走之后的答案。我们显然不能把所有拿走的方案枚举一遍,所以考虑计算每一个字符串的贡献。这里我的贡献指第i个字符串与它前面的字符串的贡献。而这个贡献就是计算当前串与前面所有串的lcp。这里千万不要看到lcp就去想后缀数组,这里是多个串的lcp,而不是一个串的lcp,所以后缀数组显然是麻烦了。字典树是一个好的选择,但是字符串哈希显然更简单。对于每个串不断的把它所有前缀的哈希值记录下来,然后每个串的贡献,就可以看它前缀出现的次数。

然后我们考虑dp。令dp[i][j]表示考虑前i个串,从中取走j个串的答案。那么可以分为第i个串取走或者不被取走,有:

                  \large dp[i][j]=dp[i-1][j-1]+(dp[i-1][j]+C_{i-2}^{j}*f[i])*[i-j>2]

前面部分表示第i个串取走,后面部分表示第i个串不取走。不取走的话就是前面i-1个串取走j个的答案,加上第i个串对前面产生的贡献。出现这种情况的次数是 \large C_{i-1}^{j} ,第i个串对前面产生的单次总贡献是f[i],这样合起来就是\large C_{i-1}^{j}*f[i],但是由于有j个串被删除了所以得去掉一些。对于去掉的部分,我们再次考虑贡献。如果第k个串被删掉,那么多加的答案就是\large C_{i-2}^{j-1}*lcp(i,k)那么总的就是:

                          \large \sum_{k=1}^{i-1}C_{i-2}^{j-1}*lcp(i,k)=C_{i-2}^{j-1}*\sum_{k=1}^{i-1}lcp(i,k)=C_{i-2}^{j-1}*f[i]

所以说最后就是 \large C_{i-1}^{j}*f[i]-C_{i-2}^{j-1}*f[i]=C_{i-2}^j*f[i]。dp求解,然后对于每个询问O(1)输出即可。最后你会发现,答案其实就是dp[n][0]*C(n-2,j)。具体见代码:

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF ((1LL<<31)-1)
#define PI 3.1415926535
#define sf(x) scanf("%d",&x)
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/priority_queue.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
 
using namespace std;
using namespace __gnu_pbds;
 
const int N = 4010;
const int mod1 = 100001651;
const int mod2 = 100001623;
const int mod = 1000000007;
  
LL dp[N][310],c[N][N],f[N];
gp_hash_table<LL,int> t; 
char s[3000010];
int n,q;
 
LL add(LL pre1,LL pre2,int cur)
{
    pre1=(pre1*111+cur)%mod1;
    pre2=(pre2*100007+cur)%mod2;
    return pre1<<31|pre2;
}
  
int main()
{
    sf(n); sf(q);
    for(int i=1;i<=n;i++)
    {
        LL now=0;
        scanf("%s",s);
        for(int j=0;s[j];j++)
        {
            now=add(now>>31,now&INF,s[j]-'a');
            f[i]=(f[i]+t[now])%mod2; t[now]++;
        }
    }
    for(int i=0;i<=n;i++) c[i][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=min(i,300);j++)
        {
            if (j) dp[i][j]=dp[i-1][j-1];
            if (i-j>=2) 
            {
                dp[i][j]=(dp[i][j]+dp[i-1][j])%mod;
                dp[i][j]=(dp[i][j]+c[i-2][j]*f[i]%mod)%mod;
            }
        }
    while(q--)
    {
        int x; sf(x);
        printf("%lld\n",dp[n][x]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82956275