• CCPC2020长春 G题 Monkey's Keyboard


    链接:
    https://codeforces.com/gym/102832/problem/G

    Pro:
    给定一个

    Sol:
    很容易想到另一个题,叫歌唱王国。

    然后按照那道题的套路推一发式子

    发现对于任意一个字符串的

    [egin{align*} ans_S&=sum_{i,i is a border of S} prod_{j=1}^i frac{1}{p_i} \ &=sum_{i,i is a border of S} w_i (写成一个前缀积的形式) end{align*} ]

    发现这个式子的贡献
    只和border有关
    然后总答案的话可以考虑枚举border是谁
    然后算这个border的贡献是多少
    具体地说
    首先,枚举border显然可以用SAM来实现
    然后考虑贡献怎么计算
    简单推到即可发现
    对于SAM上任意一个节点(x)
    其贡献为

    [egin{align*} ans&=sum_{len=a}^bsum_{r=1}^{cnt[x]} sum_{l=1}^r frac{w_{pos}}{w_{pos-len}} \ &=frac{cnt_x*(cnt_x+1)}{2}*sum_{len=a}^bfrac{w_{pos}}{w_{pos-len}} \ &=frac{cnt_x*(cnt_x+1)}{2}*{w_{pos}}*sum_{len=a}^bfrac{1}{w_{pos-len}} end{align*} ]

    对于w数组的倒数搞一个前缀和即可

    #include<bits/stdc++.h>
    #define N 1100000
    #define db double
    #define ll long long
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,mo=1e9+7;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int ksm(int x,int k)
    {
    	int ans=1;
    	while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
    	return mod(ans);
    }
    int inv(int x){return ksm(x,mo-2);}
    int read()
    {
    	char ch=0;int x=0,flag=1;
    	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
    	return x*flag;
    }
    void write(int x)
    {
    	if(!x)return (void)putchar(48);
    	if(x<0)putchar(45),x=-x;
    	int len=0,p[20];
    	while(x)p[++len]=x%10,x/=10;
    	for(int i=len;i>=1;i--)putchar(p[i]+48);
    }
    const db eps=1e-7,inf=1e9+7,pi=acos(-1);
    db Read(){db x;scanf("%lf",&x);return x;}
    void Write(db x){printf("%lf",x);}
    char ch[N];
    int root=1,size=1,last=1,sz[N];
    struct node{int pos,len,lnk,nxt[26];}s[N];
    void insert(int k)
    {
        int cur=++size,p=last;last=cur;
       	sz[cur]=1;s[cur].pos=s[cur].len=s[p].len+1;
        while(p&&!s[p].nxt[k])s[p].nxt[k]=cur,p=s[p].lnk;
        if(!p){s[cur].lnk=root;return;}
        int q=s[p].nxt[k];
        if(s[p].len+1==s[q].len)s[cur].lnk=q;
        else
        {
            int clone=++size;
            s[clone]=s[q];s[clone].len=s[p].len+1;
            while(p&&s[p].nxt[k]==q)s[p].nxt[k]=clone,p=s[p].lnk;
            s[q].lnk=s[cur].lnk=clone;
        }
    }
    struct edge{int to,nxt;}e[N];
    int num,head[N];
    void add(int x,int y){e[++num]={y,head[x]};head[x]=num;}
    void dfs(int x,int fa)
    {
    	for(int i=head[x];i!=-1;i=e[i].nxt)
    	{
    		int to=e[i].to;
    		if(to==fa)continue;
    		dfs(to,x);sz[x]+=sz[to];
    	}
    }
    int p[N],w[N],v[N];
    int main()
    {
    	scanf("%s",ch+1);
    	int n=strlen(ch+1),tot=0,ans=0;
    	for(int i=1;i<=n;i++)insert(ch[i]-'a');	
    	for(int i=0;i<26;i++)p[i]=read(),tot=inc(tot,p[i]);
    	for(int i=0;i<26;i++)p[i]=1ll*tot*inv(p[i])%mo;
    	w[0]=1;for(int i=1;i<=n;i++)w[i]=1ll*w[i-1]*p[ch[i]-'a']%mo;
    	v[0]=1;for(int i=1;i<=n;i++)v[i]=inc(v[i-1],inv(w[i]));
    	num=-1;memset(head,-1,sizeof(head));
    	for(int x=2;x<=size;x++)add(s[x].lnk,x);dfs(1,1);
    	for(int x=2;x<=size;x++)
    	{
    		int a=s[s[x].lnk].len+1,b=s[x].len;
    		int l=s[x].pos-b,r=s[x].pos-a;
    		int t=1ll*(sz[x]+1)*sz[x]%mo*inv(2)%mo*w[s[x].pos]%mo; 
    		if(r>=0)ans=inc(ans,1ll*t*v[r]%mo);
    		if(l-1>=0)ans=dec(ans,1ll*t*v[l-1]%mo);
    	}
    	write(ans);
    	return 0;
    }
    
  • 相关阅读:
    Javascript 正确用法 二
    c# 未能载入文件或程序集
    Linux系统备份
    环保创业的可行之道——Leo鉴书上66
    Oracle的序列
    UVA 10574
    网页内容的html标签补全和过滤的两种方法
    使用POI来实现对Excel的读写操作
    OVER(PARTITION BY)函数介绍
    Kill 正在执行的存储过程
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/14100040.html
Copyright © 2020-2023  润新知