【题目】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; }