• 联考20200721 T2 s2mple



    分析:
    什么神仙题,考场上暴力都不会写
    (深深地明白了自己是个废物的事实)
    我们转换一下统计方式,假设要查询的串为(P),在前面加上一个串(Q),在后面加上一个串(R),得到的新串为(T),与前面的串Q形成一个二元组((T,Q))
    答案即为本质不同的二元组((T,Q))个数,其中(T)(S)的子串
    (感性理解一下发现很对(捂脸)
    先统计(Q+P)的个数
    构建一个SAM,答案即为fail树上(P)所在节点的后代的所有子串加上(P)所在节点集合长度不小于(|P|)的子串
    所以该节点对权值的贡献可以表示为(−|P|+b),其中(b)是一个常数,我们可以事先处理出来。
    (P)后面接(R)就是沿着SAM的边走,知道(Q+P)的数量后实际上是一个DP,一条边的起点会对终点做贡献,由于事先我们不知道(Q+P)的数量,但是由于其可以写成(b-|P|)的形式
    那么某一个点的值会被写成(k|P|+b)的形式,我们沿着边维护每个点的(k,b)即可
    转移时注意(k|P|+b)会变成(k(|P|+1)+b=k|P|+(k+b))
    每次询问找到|P|对应的节点,把|P|代进去算即可,这个可以在fail树上倍增找

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<vector>
    #include<string>
    
    #define maxn 1000005
    #define INF 0x3f3f3f3f
    #define MOD 998244353
    
    using namespace std;
    
    inline int getint()
    {
    	int num=0,flag=1;char c;
    	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    	return num*flag;
    }
    
    int n,Q;
    char s[maxn];
    struct node{
    	int len,nxt[26],fa;
    }t[maxn];
    int lst=1,tot=1;
    struct cp{
    	long long b,k;
    	cp(){}
    	cp(long long x,long long y){b=x,k=y;}
    	friend cp operator +(cp x,cp y){return cp(x.b+y.b,x.k+y.k);}
    }f[maxn];
    int d[maxn];
    long long sz[maxn];
    int F[maxn][20],pos[maxn];
    inline bool cmp(int x,int y)
    {return t[x].len<t[y].len;}
    
    inline void ins(int c)
    {
    	int p=lst,np=lst=++tot;
    	t[np].len=t[p].len+1;
    	while(p&&!t[p].nxt[c])t[p].nxt[c]=np,p=t[p].fa;
    	if(!p)t[np].fa=1;
    	else
    	{
    		int q=t[p].nxt[c];
    		if(t[q].len==t[p].len+1)t[np].fa=q;
    		else
    		{
    			int nq=++tot;
    			memcpy(t[nq].nxt,t[q].nxt,sizeof t[q].nxt);
    			t[nq].fa=t[q].fa,t[nq].len=t[p].len+1;
    			t[q].fa=t[np].fa=nq;
    			while(p&&t[p].nxt[c]==q)t[p].nxt[c]=nq,p=t[p].fa;
    		}
    	}
    }
    
    inline int find(int x,int l)
    {
    	for(int i=19;~i;i--)if(F[x][i]&&t[F[x][i]].len>=l)x=F[x][i];
    	return x;
    }
    
    int main()
    {
    	n=getint(),Q=getint();
    	scanf("%s",s+1);
    	for(int i=1;i<=n;i++)ins(s[i]-97),pos[i]=lst;
    	
    	for(int i=1;i<=tot;i++)
    	{
    		d[i]=i;
    		sz[i]=t[i].len-t[t[i].fa].len;
    		if(t[i].fa)F[i][0]=t[i].fa;
    	}
    	
    	for(int j=1;j<20;j++)for(int i=1;i<=tot;i++)
    		F[i][j]=F[F[i][j-1]][j-1];
    	sort(d+1,d+tot+1,cmp);
    	for(int i=tot;i;i--)
    	{
    		int x=d[i];sz[t[x].fa]+=sz[x];
    		f[x]=cp(sz[x]+t[t[x].fa].len+1,-1);
    	}
    	for(int i=tot;i;i--)
    	{
    		int x=d[i];
    		for(int j=0;j<26;j++)if(t[x].nxt[j])
    		{
    			int v=t[x].nxt[j];
    			f[x]=f[x]+cp(f[v].b+f[v].k,f[v].k);
    		}
    	}
    	while(Q--)
    	{
    		int l=getint(),r=getint();
    		int tmp=find(pos[r],r-l+1);
    		printf("%lld
    ",f[tmp].b+f[tmp].k*(r-l+1));
    	}
    }
    

  • 相关阅读:
    适配器模式
    自己DIY word2010脚注和尾注没有的格式
    Linux单网卡,双IP,双网关配置,并搭建squid proxy上网
    about using gnuplot
    ReadDirectoryChangesW 函数 流沙
    Jquery easyui 异步树 流沙
    Overlapped I/O 学习 流沙
    jQuery.get(url,[data],[callback]) 流沙
    MsgWaitForMultipleObjectsEx用法 流沙
    Oracle smon_scn_time 表 说明
  • 原文地址:https://www.cnblogs.com/Darknesses/p/13362461.html
Copyright © 2020-2023  润新知