题解 P6396 【要有光】

题目传送门

好的阅读体验

真是神题

题意简述

好一道翻译题

一个字符串 \(S_0\) ,并令 \(S=S_0\) ,对 \(S\) 进行若干操作。

有五种操作:

  • 光归:若 \(S\) 非空,可将 \(S\) 变为 \(S\) 的最长回文后缀,代价 \(A\)
  • 光辉:若 \(S\)回文串,且存在 \(T\) (可为空串)为 \(S_0\) 的子串,\(S\)\(T\) 的最长回文后缀,可将 \(S\) 变为 \(T\) ,代价 \(B\)
  • 光隐:若 \(S\)非空回文串,可删去其长度相等且长度不大于 \(k\) 的前缀与后缀(删去后不能为空串),代价 \(C\)
  • 光腾:若 \(S\)非空回文串,且在 \(S\) 两端加上一对反串后生成的新回文串 \(T\)\(S_0\) 的子串,可将 \(S\) 变为 \(T\) ,代价 \(D\)
  • 光戈:若 \(S\) 非空,可在 \(S\) 前加入任意字符,代价 \(E\)。使用该操作后不能再进行其它操作。

\(q\) 次询问,求将 \(S\) 变为 \(S_0[l,r]\) 的最小代价。

\(1 \leq q,|S| \leq 10^5\)\(1 \leq k \leq 5\)\(\sum \leq 52\)

题目分析

主题是回文串,当然得在回文自动机上处理。

考虑在回文自动机的节点间建单向边,然后跑单源最短路。

记边为 \((u,v,w)\)

  • 光归\((i,fail_i,A)\)\(fail_i\) 可以为 \(0\)
  • 光辉\((fail_i,i,B)\)\(fail_i\) 可以为 \(0\)
  • 光隐\((i,pth-fa_i,C)\)\(p=1,2,\cdots,k\)\(pth-fa_i\) 表示 \(p\) 级祖先,不能为 \(0\)
  • 光腾:将 \(i\)\(fail\) 树中它的子树中所有节点连边 \((i,son,D)\)

这样会连出 \(O(|S|^2)\) 条边,不行。

可以对每个点 \(i\) 建一个虚点 \(i'\),将 \(i'\) 建按照 \(fail\) 树连边,边权为 \(0\)。注意:\(i\) 并没有按照 \(fail\) 树连边

然后对于每个点,连边 \((i',i,0)\) ,这样虚点可以随意到达它;连边 \((i,i',D)\) ,这样它就可以花费 \(D\) 的代价到达虚点,从而到达子树中的所有点。

光戈操作较为特殊,只要使用代价就已确定,故在询问时再考虑。


跑一遍 Dijkstra,得到 \(dis(i)\) ,表示到达 \(i\) 状态最小的代价。

注意有个小细节:\(S_0\) 本身不是回文串,需要先进行一次光归将其变到 PAM 的节点上,因为此时不能进行其它操作

接下来考虑询问。首先如果询问是 \(S_0[1,n]\) 需要特判,答案为 \(0\)

由于光戈操作只能由回文串在前面添加字符而来,故开始光戈前一定是询问串的一个回文后缀。

可以从询问串的最长后缀跳 \(fail\),假设跳到了节点 \(p\)满足 \(len_p \leq r-l+1\)

\[ans=\min_{p}\{(r-l+1-len_p)E+dis(p) \} \]

暴力跳 \(fail\) 仍然是不可取的 虽然能水到 85 pts ,会被链的情况卡到 \(O(|S|^2)\)

但我们发现跳 \(fail\)\(len\) 是在不断减小的,也就是查询的实际上是从根出发的一条链的最小值。

\[ans=(r-l+1)E + \min_{p}\{dis(p)-len_pE \} \]

那倍增出第一个 \(len_p \leq r-l+1\),记由根出发(不包括 0 号节点)到 \(u\) 节点的链上, \(g_u=dis(p)-len_pE\) ,答案即为

\[ans=(r-l+1)E + g_u \]

时间复杂度 \(O(k|S|+(k|S|+q)logn)\)

代码

#include<bits/stdc++.h>
using namespace std;
const long long N=4e5+5;
long long m,k,A,B,C,D,E;

struct nodd{
	long long to,nxt;
	long long w;
}e[N*2];
long long head[N],cntt;

void add(long long u,long long v,long long w){
	e[++cntt].to=v;
	e[cntt].w=w;
	e[cntt].nxt=head[u];
	head[u]=cntt;
}

//建图 

struct nod{
	int ch[52];
	int len,fail,fa;
	nod(){
		memset(ch,0,sizeof(ch));
		len=fail=0;
	}
}t[N>>1];

char s[N];
long long lst=0,cnt=1;
long long trans(char c){
	if(c>='a' && c<='z') return c-'a';
	else return c-'A'+26;
}
//转换字符 
void PAM(long long n){
	long long p=lst;
	long long c=trans(s[n]);
	while(s[n-t[p].len-1]!=s[n]) p=t[p].fail;
	if(!t[p].ch[c]){
		cnt++;
		long long q=t[p].fail;
		while(s[n-t[q].len-1]!=s[n]) q=t[q].fail;
		t[cnt].fail=t[q].ch[c];
		t[cnt].len=t[p].len+2;
		
		t[p].ch[c]=cnt;
		t[cnt].fa=p;
	}
	lst=t[p].ch[c];
}

struct abc{
	long long num,dis;
};
long long dis[N];
bool vis[N];
bool operator <(abc x,abc y){
	return x.dis>y.dis;
}
priority_queue <abc> q;

long long pre[N],ex;

long long f[N][19],g[N];
//g: 根节点(不含0)到此的 dis[p]-t[p].len*E 最小值 

void dfs(long long u){
	for(long long i=head[u];i;i=e[i].nxt){
		long long v=e[i].to;
		f[v][0]=u;
		g[v]=min(g[u],dis[v]-t[v].len*E);
		dfs(v);
	}
}
//预处理 f和 g 

int main(){
	scanf("%s",s+1);
	long long n=strlen(s+1);
	cin>>k>>A>>B>>C>>D>>E;
	
	t[0].fail=t[1].fail=1;
	t[1].len=-1;
	for(long long i=1;i<=n;i++) PAM(i),pre[i]=lst;
	//PAM
	
	if(t[lst].len!=n) ex=A;
	for(long long i=2;i<=cnt;i++)
		add(i,t[i].fail,A);
	for(long long i=2;i<=cnt;i++)
		add(t[i].fail,i,B);
	for(long long i=2;i<=cnt;i++){
		long long x=t[i].fa;
		for(long long j=1;x && j<=k;x=t[x].fa,j++)
			add(i,x,C);
	}
	
	for(long long i=2;i<=cnt;i++)
		add(i+cnt,i,0);
	for(long long i=2;i<=cnt;i++)
		add(i,i+cnt,D);
	for(long long i=2;i<=cnt;i++)
		if(t[i].fa>1)
			add(t[i].fa+cnt,i+cnt,0);
	//虚点 
	//建图 
	
	memset(dis,0x7f,sizeof(dis));
	dis[lst]=0;
	q.push((abc){lst,0});
	while(!q.empty()){
		abc tmp=q.top();
		q.pop();
		long long u=tmp.num;
		if(vis[u]) continue;
		vis[u]=1;
		for(long long i=head[u];i;i=e[i].nxt){
			long long v=e[i].to;
			if(vis[v]) continue;
			if(dis[u]+e[i].w<dis[v]) dis[v]=dis[u]+e[i].w;
			q.push((abc){v,dis[v]});
		}
	}
	//Dijkstra 
	
	memset(head,0,sizeof(head));
	cntt=0;
	for(long long i=2;i<=cnt;i++)
		add(t[i].fail,i,0);
	g[0]=1e18;
	dfs(0);
	for(long long j=1;j<=18;j++)
		for(long long i=0;i<=cnt;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	cin>>m;
	for(long long i=1;i<=m;i++){
		long long l,r;
		scanf("%lld%lld",&l,&r);
		if(l==1 && r==n){
			puts("0");
			continue;
		}
		//特判 
		long long p=pre[r];
		if(t[p].len>r-l+1){
			for(long long j=18;j>=0;j--)
				if(t[f[p][j]].len>r-l+1) p=f[p][j];
			p=f[p][0];
		}
		//倍增 
		printf("%lld\n",(r-l+1)*E+g[p]+ex);
	}
}
posted @ 2021-01-28 20:22  苹果蓝17  阅读(201)  评论(0编辑  收藏  举报