[HAOI2017]供给侧改革

Posted by Dispwnl on April 28, 2019

题目

题解

首先一个暴力做法就是把这题看成这题,然后暴力枚举就行了

发现有个随机条件……

你揣兜估计一下既然随机还只有$01$,$lcp$长度应该不大长,写个程序跑跑发现好像不大于$40$

那么就用$Trie$来维护$lcp$,枚举每一位和往后的$40$位,在$Trie$加入这$40$个串,每个点位置维护这个子串出现的最右位置,然后维护当前点代表的后缀对于每个$lcp$能找到的最右端点(代表的后缀),然后统计一下即可

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<set>
# include<algorithm>
using namespace std;
const int MAX=1e5+5;
struct p{
	int l,r,id;
	bool operator< (const p &a)
	const{
		return r<a.r;
	}
}qu[MAX];
int n,Q,tot;
int f[41],ans[MAX];
char a[MAX];
struct Trie{
	int id[MAX*40];
	int son[MAX*40][2];
	void ins(int l)
	{
		for(int i=l,x=0;i<=l+40&&i<=n;++i)
		  {
		  	if(!son[x][a[i]-'0']) son[x][a[i]-'0']=++tot;
		  	x=son[x][a[i]-'0'],f[i-l+1]=max(f[i-l+1],id[x]),id[x]=l;
		  }
	}
}_T;
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
int main()
{
	n=read(),Q=read(),scanf("%s",a+1);
	for(int i=1;i<=Q;++i)
	  qu[i].l=read(),qu[i].r=read(),qu[i].id=i;
	sort(qu+1,qu+1+Q);
	for(int i=1,L=1;i<=n;++i)
	  {
	  	_T.ins(i);
	  	while(i==qu[L].r&&L<=Q)
	  	{
	  		for(int j=1;j<=40;++j)
	  		  if(f[j]>=qu[L].l) ans[qu[L].id]+=j*(f[j]-max(f[j+1],qu[L].l-1));
	  		  else break;
	  		++L;
		}
	  }
	for(int i=1;i<=Q;++i)
	  printf("%d\n",ans[i]);
	return 0;
}