JZOJ6682. 【2020.06.04省选模拟】串在哪(string)

Description

在这里插入图片描述
n<=105,Bi<=2e5n<=10^5,\sum|Bi|<=2e5

  • 原题GDSOI2019 D2 T3 Novel

Solution

  • 相当巧妙的AC自动机上的DP。
  • 首先对于这一类分数规划的问题一般都要二分答案,然后化一下式子变成Wl,rmidlen>=0W_{l,r}-mid*len>=0,也就是长度每多1,贡献都要减去midmid,求最大值。这样子就不需要记录起点了。
  • 显然建一个AC自动机,直接设状态f[i]f[i]表示AC自动机上以ii为结尾的字符串最大的Wl,rmidlenW_{l,r}-mid*len
  • 既然是在AC自动机上DP,那么考虑下一个字符的节点是xx,那么考虑f[x]f[x]的值,首先不难想到它直接从f[fail[x]]f[fail[x]]转移过来,但是这样还有一段起点不会考虑到。
  • 因此再设一个状态g[x]g[x]表示从fail[x]fail[x]前为起点,到xx的最大值,那么f[x]=max(g[x],f[fail[x]])f[x]=max(g[x],f[fail[x]])
  • 考虑g[x]g[x]的转移,设yyxx的父亲,那么可以发现在yy跳fail的过程中,这些节点fail[y],fail[fail[y]],fail[...]fail[y],fail[fail[y]],fail[...]gg所覆盖的起点区间刚好是不相交的,所以直接考虑将这些gg转移过来,再加上以当前位置为末尾的字符串的贡献sum[fail[x]]midsum[fail[x]]-mid
  • 这样一直跳到一个yy使得yy有儿子zzxx匹配,即fail[x]=zfail[x]=z
  • 这就是一个跳fail的过程,并且所有加进去的gg都包括fail[x]fail[x],所以时间复杂度和正确性有了保证。
  • 注意没有计算到整个串xx的贡献,单独补上。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 300005
#define ll long long 
#define db double 
using namespace std;

int n,q,m,p,i,j,k,x,y,z;
int id,tot,tr[maxn][26],fail[maxn],dep[maxn];
db f[maxn],g[maxn],sum[maxn],h[maxn];

int t,w,d[maxn],fa[maxn];
void prepare(){
	for(i=0;i<26;i++) tr[0][i]=1;
	t=0,w=1,d[1]=1;
	while (t<w){
		x=d[++t];
		for(i=0;i<26;i++) if (tr[x][i]){
			y=tr[x][i],dep[y]=dep[x]+1,d[++w]=y;
			for(z=fail[x];!tr[z][i]&&z;z=fail[z]);
			fail[y]=tr[z][i];
			sum[y]+=sum[fail[y]],h[y]=h[x]+sum[y];
		}
	}
}

int check(db mid){
	t=0,w=1,d[1]=1;
	for(i=1;i<=tot;i++) f[i]=g[i]=-1e15;
	while (t<w){
		x=d[++t];
		for(i=0;i<26;i++) if (tr[x][i]){
			y=tr[x][i];
			g[y]=max(g[y],g[x]-mid+sum[fail[y]]);
			for(z=fail[x];!tr[z][i]&&z;z=fail[z])
				g[y]=max(g[y],g[z]-mid+sum[fail[y]]);
			g[y]=max(g[y],h[y]-mid*dep[y]);
			f[y]=max(f[fail[y]],g[y]);
			d[++w]=y;
		}
	}
	db mx=-1e15;
	for(x=id;x;x=fa[x]) 
		mx=max(mx,f[x]);
	return mx>=0;
}

int main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	char ch=getchar();
	tot=x=1;
	while (ch>='a'&&ch<='z') {
		if (!tr[x][ch-'a']) tr[x][ch-'a']=++tot;
		fa[tr[x][ch-'a']]=x,x=tr[x][ch-'a'],ch=getchar();
	} id=x;
	scanf("%d",&m);
	while (m--){
		for(ch=getchar();ch<'a'||ch>'z';ch=getchar());
		x=1; 
		while (ch>='a'&&ch<='z') {
			if (!tr[x][ch-'a']) tr[x][ch-'a']=++tot;
			fa[tr[x][ch-'a']]=x,x=tr[x][ch-'a'],ch=getchar();
		}
		scanf("%d",&k),sum[x]+=k;
	}
	prepare();
	db L=0,R=1e10,mid,E=1e-6;
	while (L+E<R){
		mid=(L+R)/2;
		if (check(mid)) L=mid;
		else R=mid;
	}
	printf("%.4lf",R);
}
posted @ 2020-06-05 22:33  Deep_Thinking  阅读(201)  评论(0编辑  收藏  举报