LOJ

题目

传送门

解法

先开始想了半天,觉得 (0,1) 一定是题目的关键突破口。我真傻。

首先,两个前缀的最长公共后缀长度可以看做 ( ext{parent tree}) 上两个节点(后文节点均在 ( ext{parent tree}) 上)的 ( ext{LCA})len

那么问题就转化成了一段区间表示的节点两两匹配的 ( ext{LCA})len 的最大值。

暴力计算一个询问就是 (mathcal O(n^2log n)) 的,我们考虑优化。

我们从 ( ext{parent tree}) 上来思考。设某子树根为 (rt),它还有子树 (x) 等若干子树,那么显然固定子树 (x) 中的点 (u),它与 (rt) 除了 (x) 外的所有子树内的点的 ( ext{LCA}) 都是 (rt),其 len 是固定的。

再回到询问区间,我们发现,假如有 ((a,b),(c,d)) 分别有同样的贡献,且 (c<a<b<d),我们只用选择 ((a,b))

据此我们发现,如果有同样的 len ,其实只用选取在 区间 上离 (u) 最近的两个点来组成点对。

set 维护节点的子树节点的序列编号,并用启发式合并就可以完成选取点对。点对数量可以用启发式合并时间复杂度来分析,是 (nlog n) 级别的。

最后将点对按较大的从小到大排序,询问按 (r) 从小到大排序(容易发现 (l) 递增是撤回操作),用树状数组和扫描线维护区间最大值即可(注意树状数组是反着搞的,因为越小的 (l) 反而包含的越多)。

代码

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}

#include <set>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn=1e5+5;

int n,m,ans[maxn],tot,c[maxn];
char str[maxn];
vector <int> e[maxn<<1];
set <int> :: iterator it,pre,nxt;
struct Node {
	int a,b,c;
	bool operator < (const Node t) const {
		return b<t.b;
	}	
} Pair[maxn*40],q[maxn];
struct SAM {
	int cnt,las;
	set <int> st[maxn<<1];
	struct node {
		int to[2],fa,len;
	} s[maxn<<1];
	
	SAM() {cnt=las=1;}
	
	void Extend(int i) {
		int cur=++cnt,p=las,c=str[i]-'0'; s[cur].len=s[las].len+1;
		for(;p && s[p].to[c]==0;p=s[p].fa) s[p].to[c]=cur;
		if(!p) s[cur].fa=1;
		else {
			int q=s[p].to[c];
			if(s[q].len==s[p].len+1) s[cur].fa=q;
			else {
				int now=++cnt; s[now]=s[q];
				s[now].len=s[p].len+1;
				s[q].fa=s[cur].fa=now;
				for(;p && s[p].to[c]==q;p=s[p].fa) s[p].to[c]=now;
			}
		}
		las=cur; st[cur].insert(i);
	}
	
	void Link() {
		rep(i,2,cnt) e[s[i].fa].push_back(i);
	}
	
	void dfs(int u) {
		for(int i=0;i<e[u].size();++i) {
			int v=e[u][i];
			dfs(v);
			if(s[u].len==0) continue;
			if(st[u].size()<st[v].size()) st[u].swap(st[v]);
			for(it=st[v].begin();it!=st[v].end();++it) {
				st[u].insert(*it);
				pre=nxt=st[u].find(*it); ++nxt;
				if(pre!=st[u].begin()) --pre,Pair[++tot]=(Node){*pre,*it,s[u].len};
				if(nxt!=st[u].end()) Pair[++tot]=(Node){*it,*nxt,s[u].len};
				st[u].erase(*it);
			}
			for(it=st[v].begin();it!=st[v].end();++it) st[u].insert(*it);
		}
	}
} mac;

int lowbit(int x) {return x&-x;}

void add(int x,int val) {
	while(x) c[x]=Max(c[x],val),x-=lowbit(x);
}

int ask(int x) {
	int ret=0;
	while(x<=n) ret=Max(ret,c[x]),x+=lowbit(x);
	return ret;
}

int main() {
	int Pointer=0;
	n=read(9),m=read(9),scanf("%s",str+1);
	rep(i,1,n) mac.Extend(i);
	mac.Link(); mac.dfs(1);
	rep(i,1,m) q[i].a=read(9),q[i].b=read(9),q[i].c=i;
	sort(q+1,q+m+1),sort(Pair+1,Pair+tot+1);
	rep(i,1,m) {
		while(Pointer<tot && Pair[Pointer+1].b<=q[i].b) ++Pointer,add(Pair[Pointer].a,Pair[Pointer].c);
		ans[q[i].c]=ask(q[i].a);
	}
	rep(i,1,m) print(ans[i],'
');
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/14422890.html