1. 程式人生 > 實用技巧 >「雅禮集訓 2017 Day7」事情的相似度

「雅禮集訓 2017 Day7」事情的相似度

一、題目

點此看題

二、題目

字尾自動機亂殺。

他問的是字首之間的最長字尾,我們對正串建出字尾自動機,然後把字首在自動機上面打上標記。根據字尾自動機的性質,最長字尾就是兩個字首在 \(\tt parent \;tree\)\(\tt lca\)\(len\)

對於一個字首對 \((l,r)\) ,那麼他可以對 \(L\leq l,r\leq R\) 的詢問 \((L,R)\) 產生貢獻。因為是 \(\tt lca\) ,所以可以想到通過樹上啟發式合併找出這些字首對,那麼我們在合併子樹的時候就要考慮子樹之間新產生的點對。

貌似這樣字首對的數量是 \(O(n^2)\) 的,合併的時候我們只需要找最接近的字首,因為貢獻都是一樣的,但是找最接近的能貢獻到的是最多的,所以數量是 \(O(n\log n)\)

,用 \(\tt set\) 維護啟發式合併。

那麼時間複雜度 \(O(n\log^2 n)\),最後算貢獻的時候本質上是一個二維偏序,所以直接排序就行了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,tot,cnt,last,bit[M],f[M],ans[M];char t[M];
set<int> s[M];
struct edge
{
	int v,next;
	edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
struct node
{
	int fa,ch[2],len;
}a[M];
struct zxy
{
	int l,r,x;
	bool operator < (const zxy &b) const
	{
		return r<b.r;
	}
}q[M],p[20*M];
void add(int c)
{
    int p=last,np=last=++cnt;
    a[np].len=a[p].len+1;
    for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
    if(!p) a[np].fa=1;
    else
    {
        int q=a[p].ch[c];
        if(a[q].len==a[p].len+1) a[np].fa=q;
        else
        {
            int nq=++cnt;
            a[nq]=a[q];a[nq].len=a[p].len+1;
            a[q].fa=a[np].fa=nq;
            for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
        }
    }
}
void dfs(int u,int fa)
{
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);
		if(s[u].size()<s[v].size()) swap(s[u],s[v]);
		set<int>::iterator it,pre,nxt;
		for(it=s[v].begin();it!=s[v].end();it++)
		{
			s[u].insert(*it);
			pre=nxt=s[u].find(*it);nxt++;
			if(pre!=s[u].begin()) pre--,p[++k]=zxy{*pre,*it,a[u].len};
			if(nxt!=s[u].end()) p[++k]=zxy{*it,*nxt,a[u].len};
			s[u].erase(*it); 
		}
		for(it=s[v].begin();it!=s[v].end();it++)
			s[u].insert(*it);
	}
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int y)
{
	for(int i=x;i>=1;i-=lowbit(i))
		bit[i]=max(bit[i],y);
}
int ask(int x)
{
	int res=0;
	for(int i=x;i<=n;i+=lowbit(i))
		res=max(res,bit[i]);
	return res;
}
signed main()
{
	n=read();m=read();
	scanf("%s",t+1);
	cnt=last=1;
	for(int i=1;i<=n;i++)
	{
		add(t[i]-'0');
		s[last].insert(i);
	}
	for(int i=2;i<=cnt;i++)
	{
		int j=a[i].fa;
		e[++tot]=edge(i,f[j]),f[j]=tot;
		e[++tot]=edge(j,f[i]),f[i]=tot;
	}
	dfs(1,0);
	sort(p+1,p+1+k);
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read();
		q[i]=zxy{l,r,i};
	}
	sort(q+1,q+1+m);
	for(int i=1,j=1;i<=m;i++)
	{
		while(j<=k && p[j].r<=q[i].r) add(p[j].l,p[j].x),j++;
		ans[q[i].x]=ask(q[i].l);
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}