2018-2019 ACM-ICPC, Asia East Continent Finals J. Philosophical … Balance

题面

题意

多组数据,每组数据给出一个长度为n的字符串S,求 max ⁡ i ∑ j = 1 n l c p ( i , j ) ∗ k j \max_{i}{\sum_{j=1}^{n}lcp(i,j)*k_{j}} maxij=1nlcp(i,j)kj的最小值
其中 k i k_{i} ki由你定,但是要满足 ∑ i = 1 n k i = 1 \sum_{i=1}^{n}k_{i}=1 i=1nki=1,且 0 < = k i < = 1 0<=k_{i}<=1 0<=ki<=1

做法

首先用后缀自动机对字符串的反串建出后缀树,然后考虑计算在后缀树中的每棵子树怎么分配k值。
对于一棵子树,如果根节点u是后缀节点,则很显然,如果这棵子树到的k值之和为x,那么根节点的答案就是 ∑ l c p ( i , u ) ∗ x \sum{lcp(i,u)*x} lcp(i,u)x,且 l c p ( i , u ) = l e n [ u ] lcp(i,u)=len[u] lcp(i,u)=len[u](先不考虑子树外的串的贡献),而且不难发现这是子树中所有串中最大的。
如果根节点u不是后缀节点,那么就要考虑将它的几个子节点的子树合并,我们已经递归求出了它的每棵子树的 k k k,表示如果分配给这棵子树 x x x,它的最小值将是 k ∗ x k*x kx,为了使所有子树的最大值最小,从那什均衡的角度思考,最好让它们都相等,如果分配给子树v,a的权值,那么它的贡献就是
a ∗ k [ v ] a*k[v] ak[v](内部贡献) + ( 1 − a ) ∗ l e n [ u ] +(1-a)*len[u] +(1a)len[u](子树之间的贡献)
只要让几棵子树的这个值相等即可。

代码

#include
#define db double
#define N 400100
using namespace std;

int T,n,tt,last;
char str[N];
db K[N];
struct Node
{
    int to[26],len,pa;
    bool bj;
}node[N<<1];
vector<int>to[N];

inline void add(int u)
{
    int p=last,np=++tt;
    last=np;
    node[np].len=node[p].len+1;
    for(;p&&!node[p].to[u];p=node[p].pa) node[p].to[u]=np;
    node[np].bj=1;
    if(!p)
    {
		node[np].pa=1;
		return;
    }
    int q=node[p].to[u];
    if(node[q].len==node[p].len+1)
    {
		node[np].pa=q;
		return;
    }
    int nq=++tt;
    node[nq].len=node[p].len+1;
    node[nq].pa=node[q].pa;
    memcpy(node[nq].to,node[q].to,sizeof(node[q].to));
    node[np].pa=node[q].pa=nq;
    for(;node[p].to[u]==q;p=node[p].pa) node[p].to[u]=nq;
}

void dfs(int now)
{
    if(node[now].bj)
    {
		K[now]=node[now].len;
		return;
    }
    int i,t,p;
    db sum=0;
    for(i=0;i<to[now].size();i++)
    {
		t=to[now][i];
		dfs(t);
		if(!i) sum+=1,p=t;
		else sum+=(K[p]-node[now].len)/(K[t]-node[now].len);
    }
    sum=1.0/sum;
    K[now]=(1-sum)*node[now].len+sum*K[p];
}

int main()
{
    int i,j;
    cin>>T;
    while(T--)
    {
		for(i=1;i<=tt;i++)
		{
		    to[i].clear();
		    node[i].pa=0;
		    node[i].len=0;
		    node[i].bj=0;
		    memset(node[i].to,0,sizeof(node[i].to));
		}
		tt=last=1;
		scanf("%s",str+1);
		n=strlen(str+1);
		reverse(str+1,str+n+1);
		for(i=1;i<=n;i++) add(str[i]-'a');
		for(i=2;i<=tt;i++) to[node[i].pa].push_back(i);
		dfs(1);
		printf("%.10f\n",K[1]);
    }
}

你可能感兴趣的:(树,经典,技巧,自动机,字符串,博弈,dp)