2019 牛客国庆集训派对day1-C Distinct Substrings(exkmp+概率)

链接:https://ac.nowcoder.com/acm/contest/1099/C
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
Special Judge, 64bit IO Format: %lld

题目描述

 

 

输入描述:

 

 

输出描述:

For each test case, print an integer which denotes the result.
示例1

输入

复制
3 2
1 1 2
2 3
1 2
1 1000000
1

输出

复制
18
69
317072014

备注:

For the second test case, h(1) = h(2) = 2, h(3) = 3.


题目的意思是:给一串数字,然后在后面添加一个数字x问你新增了多少子串h[x],求这个数字x取从1到m时 3x*h[x]%mod的异或和;

思路:考虑新添加一个数字,增加的子串的数量为所有以该数字结尾的后缀,但是会出现前面已经出现过的子串,所以我们需要算出在没有添加之前出现过多少以该数字结尾的字符串,考虑将这些数反过来,然后用exkmp求出,next[i]: T[i~n-1]和T的最长公共前缀;对于1~m的每一个数字,我们只要求出其最大值即可,然后n-最大值就是这个数字的答案,将所有的答案异或就是最后的答案了。


参考代码:

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
const int N=1e6+10;
int x[N],y[N],w[N],n,m,nxt[N],ex[N];
void pre_exkmp()
{
    nxt[0]=n;
    int j=0;
    while(j+1<n&&x[j]==x[j+1])j++;
    nxt[1]=j;
    int k=1;
    for(int i=2;i<n;i++)
    {
        int p=nxt[k]+k,t=nxt[i-k];
        if(i+t<p)nxt[i]=t;
        else 
        {
            j=p-i<0?0:p-i;
            while(i+j<n&&x[i+j]==x[j])j++;
            nxt[i]=j;k=i;
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<=n-1;++i) scanf("%d",&x[n-i-1]);
        pre_exkmp();
        for(int i=1;i<=m;++i) w[i]=-1;
        nxt[n]=0;
        for(int i=0;i<=n-1;++i) w[x[i]]=max(nxt[i+1],w[x[i]]);
        int ans=0,p=1;
        for(int i=1;i<=m;++i)
        {
            p=1ll*p*3%mod;
            ans^=1ll*p*(n-w[i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 



posted @ 2019-10-09 21:18  StarHai  阅读(340)  评论(0编辑  收藏  举报