CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP

Code:

#include <bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 400004
#define ll long long
using namespace std;
int edges,n,Q;
int hd[maxn],to[maxn],nex[maxn],tr[maxn];
char str[maxn];
void addedge(int u,int v,int c)
{
	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
namespace SAM
{
	int last, tot;
	int len[maxn],f[maxn],trans[maxn][27];
	void init() { last=tot=1; }
	int extend(int c)
	{
		int np=++tot,p=last;
		len[np]=len[p]+1,last=np;
		while(p&&!trans[p][c]) trans[p][c]=np, p=f[p];
		if(!p) f[np]=1;
		else
		{
			int q=trans[p][c];
			if(len[q]==len[p]+1) f[np]=q;
			else
			{
				int nq=++tot;
				len[nq]=len[p]+1;
				memcpy(trans[nq],trans[q],sizeof(trans[q]));
				f[nq]=f[q],f[q]=f[np]=nq;
				while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p];
			}
		}
		return np;
	}
	void build()
	{
		for(int i=2;i<=tot;++i) addedge(f[i],i,len[f[i]]-len[i]);
	}
}
int tim;
int dfn[maxn],Top[maxn],hson[maxn],siz[maxn],fa[maxn],dis[maxn];
void dfs1(int u,int ff)
{
	fa[u]=ff,siz[u]=1,dfn[u]=++tim,dis[u]=dis[ff]+1;
	for(int i=hd[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==ff) continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[hson[u]]) hson[u]=v;
	}
}
void dfs2(int u,int tp)
{
	Top[u]=tp;
	if(hson[u]) dfs2(hson[u],tp);
	for(int i=hd[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==fa[u]||v==hson[u]) continue;
		dfs2(v,v);
	}
}
int LCA(int x,int y)
{
	while(Top[x]!=Top[y])
	{
		dis[Top[x]]>dis[Top[y]]?x=fa[Top[x]]:y=fa[Top[y]];
	}
	return dis[x]<dis[y]?x:y;
}
vector<int>G[maxn];
ll ans=0;
int t=0,tot=0;
int arr[maxn],brr[maxn],A[maxn<<1],mk[maxn],mka[maxn],mkb[maxn],sizea[maxn],sizeb[maxn],S[maxn];
bool cmp(int i,int j)
{
	return dfn[i]<dfn[j];
}
void addvir(int x,int y)
{
	G[x].push_back(y);
}
void insert(int x)
{
	if(t<=1) { S[++t]=x; return; }
	int lca=LCA(S[t],x);
	if(lca==S[t]) { S[++t]=x; return; }
	while(t>1&&dis[S[t-1]]>=dis[lca]) addvir(S[t-1],S[t]),--t;
	if(S[t]!=lca) addvir(lca,S[t]),S[t]=lca;
	S[++t]=x;
}
void DP(int x)
{
	for(int i=0;i<G[x].size();++i)
		DP(G[x][i]), sizea[x]+=sizea[G[x][i]], sizeb[x]+=sizeb[G[x][i]];
	for(int i=0;i<G[x].size();++i)
	{
		ans+=1ll*(sizea[x]-sizea[G[x][i]])*sizeb[G[x][i]]*SAM::len[x];
	}
	ans+=(1ll*mka[x]*mkb[x]+1ll*mka[x]*sizeb[x]+1ll*mkb[x]*sizea[x])*SAM::len[x];
	sizea[x]+=mka[x],sizeb[x]+=mkb[x];
}
void erase(int x)
{
	mka[x]=mkb[x]=sizea[x]=sizeb[x]=0;
	for(int i=0;i<G[x].size();++i) erase(G[x][i]);
	G[x].clear();
}
void work()
{
	tot=0;
	int k1,k2;
	scanf("%d%d",&k1,&k2);
	for(int i=1;i<=k1;++i) scanf("%d",&arr[i]),A[++tot]=tr[arr[i]],mka[tr[arr[i]]]=1;
	for(int i=1;i<=k2;++i) scanf("%d",&brr[i]),A[++tot]=tr[brr[i]],mkb[tr[brr[i]]]=1;
	sort(A+1,A+1+tot,cmp);
    tot=unique(&A[1],&A[tot+1])-A-1;
    t=ans=0;
    if(A[1]!=1) S[t=1]=1;
    for(int i=1;i<=tot;++i) insert(A[i]);
    while(t>1) addvir(S[t-1],S[t]),--t;
    DP(1);
	printf("%I64d\n",ans);
	erase(1);
}
int main()
{
	// setIO("input");
	scanf("%d%d%s",&n,&Q,str+1);
	SAM::init();
	for(int i=n;i>=1;--i) tr[i]=SAM::extend(str[i]-‘a‘);
	SAM::build();
	dfs1(1,0),dfs2(1,1);
    for(int i=1;i<=Q;++i) work();
	return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/11132769.html

时间: 2024-08-28 05:55:55

CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP的相关文章

【BZOJ-2286】消耗战 虚树 + 树形DP

2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁

【BZOJ-3572】世界树 虚树 + 树形DP

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种

BZOJ 2286 树链剖分+DFS序+虚树+树形DP

第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #define LL long long 6 using namespace std; 7 const LL Maxm=501000; 8 const LL Maxn=250100; 9 const LL Inf=1e60; 1

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】

题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. 世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同.有的聚居地之间有双向的道路相连,道路的长度为1.保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环.定义两个聚居地之间的距

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[

BZOJ 2806 [Ctsc2012]Cheat ——后缀自动机 单调队列优化DP

先建出广义后缀自动机. 然后跑出文章中每一个位置的最大匹配距离. 然后定义$f[i]$表示匹配到以$i$结尾的串时,最长的匹配距离. 显然可以二分$L$的取值. 然后容易得到$DP$方程 $f[i]=max(f[i-1],f[j]+i-j)(j<=i-L)$ 然后就发现$j$属于一个区间,然后就可以单调队列优化了. #include <map> #include <ctime> #include <cmath> #include <queue> #in