【题目】D. Acyclic Organic Compounds

【题意】给定一棵带点权树,每个点有一个字符,定义一个结点的字符串数为往下延伸能得到的不重复字符串数,求min(点权+字符串数),n<=300000,time=3s。

【算法】trie合并||hash+线段树合并||dsu on tree

【题解】维护每个节点的Trie,那么每个节点的不重复字符串数是Trie的节点数。

每个节点Tire的根设为这个节点的字符(不是空字符)

这样Trie的合并就很方便了,merge(a,b)表示将b并入a下一层,假设b的根字符为c:

如果存在trans(a,c),那么累计重叠一个节点,继续合并。

否则加入trans(a,c)=b,退出。

这样能统计出总共重叠多少个节点,merge结束后在size(a)中减去。

合并的复杂度分析和线段树合并分析相同,复杂度为$O(26*n)$。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
const int maxn=300010;
int n,tot,ans=0,ansnum=0,num[maxn],first[maxn],ch[maxn][30],sz[maxn];
char s[maxn];
struct edge{int v,from;}e[maxn*2];
void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
int merge(int a,int b){
    int x=s[b]-'a',sum;
    if(ch[a][x]){
        sum=1;
        for(int i=0;i<26;i++)if(ch[b][i])sum+=merge(ch[a][x],ch[b][i]);
    }
    else{
        sum=0;
        ch[a][x]=b;
    }
    return sum;
}
void dfs(int x,int fa){
    sz[x]=1;
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        dfs(e[i].v,x);
        sz[x]+=sz[e[i].v];
        sz[x]-=merge(x,e[i].v);
    }
    if(ans<num[x]+sz[x]){
        ans=num[x]+sz[x];
        ansnum=1;
    }else if(ans==num[x]+sz[x])ansnum++;
}    
int main(){
    n=read();
    for(int i=1;i<=n;i++)num[i]=read();
    scanf("%s",s+1);
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        insert(u,v);insert(v,u);
    }
    dfs(1,0);
    printf("%d\n%d",ans,ansnum);
    return 0;
}
View Code

相关文章: