zoukankan      html  css  js  c++  java
  • 【CodeForces】601 D. Acyclic Organic Compounds

    【题目】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
    %d",ans,ansnum);
        return 0;
    }
    View Code

    hash+线段树合并:主要问题在于每次都会增加一个字符,取模后就破坏了原有顺序。

    解决方法是,每个点记录从根到它的字符串的哈希值,两个遇到一起会消去的字符串一定从根开始就相同。

    然后将这些哈希值离散化后线段树合并即可,复杂度O(n log n)。

    平衡树+启发式合并,复杂度O(n log2n)。

    注意:CodeForces卡自然溢出,可以取模1e18+7效果就和自然溢出差不多了。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #define ul unsigned long long
    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,base=23333333;
    int n,tot,cnt,first[maxn],root[maxn],num[maxn],ans=0,ansnum=0;
    ul g[maxn],h[maxn],MOD=1000000000000000009;
    char s[maxn];
    struct tree{int l,r,sum;}t[maxn*50];
    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;}
    void dfs(int x,int fa,ul num){
        g[x]=h[x]=(1ull*num*base+s[x]+233)%MOD;
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
            dfs(e[i].v,x,h[x]);
        }
    }
    void build(int l,int r,int &k,int x){
        if(!k)k=++cnt;t[k].sum=1;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)build(l,mid,t[k].l,x);
        else build(mid+1,r,t[k].r,x);
    }
    int merge(int l,int r,int a,int b){
        if(!a||!b)return a^b;
        if(l==r){t[a].sum=1;return a;}
        int mid=(l+r)>>1;
        t[a].l=merge(l,mid,t[a].l,t[b].l);
        t[a].r=merge(mid+1,r,t[a].r,t[b].r);
        t[a].sum=t[t[a].l].sum+t[t[a].r].sum;
        return a;
    }
    void ask(int x,int fa){
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
            ask(e[i].v,x);
            root[x]=merge(1,tot,root[x],root[e[i].v]);
        }
        if(num[x]+t[root[x]].sum>ans){
            ans=num[x]+t[root[x]].sum;
            ansnum=1;
        }
        else if(num[x]+t[root[x]].sum==ans)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,0);
        sort(g+1,g+n+1);
        tot=unique(g+1,g+n+1)-g-1;
        for(int i=1;i<=n;i++)h[i]=lower_bound(g+1,g+tot+1,h[i])-g;
        for(int i=1;i<=n;i++)build(1,tot,root[i],h[i]);
        ask(1,0);
        printf("%d
    %d",ans,ansnum);
        return 0;
    }
    View Code

    dsu on tree:见官方题解。

  • 相关阅读:
    项目目标文档
    系统利益相关者描述案例
    软件需求模式 读书笔记二
    软件需求分析 读书笔记1
    专业实训题目需求分析
    2015年秋季个人阅读计划
    CodeVs 1615 数据备份
    HDU 3900 Unblock Me
    HDU 5898 odd-even number
    HDU 5877 Weak Pair
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7994446.html
Copyright © 2011-2022 走看看