zoukankan      html  css  js  c++  java
  • hdu 6035 树分治

    题意:

    一棵树,路径的权值等于路径上颜色的种类,问全部路径的价值和

    思路:

    题解的方法就不多讲了,这里提供一个树分治做法,首先要将原问题转换为每种颜色的经过路径数量的总和(也就是计算每种颜色的贡献,贡献为经过这种颜色的路径数量)

    树分治的做法则为,维护重心下的子树第一次出现某种颜色的位置,通过这个位置已经这个点下面的子树大小,和一个相同颜色所有第一次出现的子树大小和来计算(注意去重),时间2s+通过,比较莽。。。毕竟比题解多个logn。。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define MEM(a,b) memset(a,b,sizeof(a))
    #define PB push_back
    typedef long long ll;
    const int maxn =1e6+10;
    ll n,k,ans;
    int root,Max;
    struct node{
        int v,next;
    }edge[maxn*2];
    int head[maxn],tot;
    int si[maxn],maxv[maxn],vis[maxn];
    int c[maxn],used[maxn],col[maxn],tmp[maxn];
    ll sum,allson;
    vector<int> us1,us2,su;
    void init(){
        tot=ans=0;MEM(head,-1);MEM(vis,0);MEM(used,0);
    }
    void add_edge(int u,int v){
        edge[tot].v=v;edge[tot].next=head[u];head[u]=tot++;
    }
    void dfssi(int u,int f){
        si[u]=1;maxv[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v==f||vis[v])continue;
            dfssi(v,u);
            si[u]+=si[v];
            if(si[v]>maxv[u])maxv[u]=si[v];
        }
    }
    void dfsroot(int r,int u,int f){
        if(si[r]-si[u]>maxv[u]) maxv[u]=si[r]-si[u];
        if(maxv[u]<Max) Max=maxv[u],root=u;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v==f||vis[v])continue;
            dfsroot(r,v,u);
        }
    }
    ll pre(int x,int fa){
        int ret=1;
        for(int i=head[x];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v!=fa&&!vis[v]) ret+=pre(v,x);
        }
        return ret;
    }
    ll dfsdis(int x, int fa) {
        ll ret=1;
        int cl=c[x];
        used[cl]++;
        for(int i=head[x];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v!=fa&&!vis[v]) ret+=dfsdis(v, x);
        }
        used[cl]--;
        if(used[cl]==0) ans+=ret*allson,tmp[cl]+=ret,us1.PB(cl),us2.PB(cl);
        return ret;
    }
    void cal(int x) {
        int id=0;
        us1.clear();su.clear();
        sum=allson=1;
        used[c[x]]++;
        for(int i=head[x];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(vis[v]) continue;
            su.PB(pre(v,x));
            allson+=su[id++];
        }
        id=0;
        for(int i=head[x];i!=-1;i=edge[i].next){
            us2.clear();
            int v=edge[i].v;
            if(vis[v]) continue;
            allson-=su[id++];
            ll son=dfsdis(v, x);
            for(int u:us2)
                ans-=tmp[u]*col[u],col[u]+=tmp[u],tmp[u]=0;
            allson+=son;
            ans+=sum*son;
            sum+=son;
        }
        for(int u:us1) col[u]=0;
        used[c[x]]--;
    }
    void dfs(int u){
        Max=n;
        dfssi(u,0);
        dfsroot(u,u,0);
        vis[root]=1;
        cal(root);
        for(int i=head[root];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(!vis[v])dfs(v);
        }
    }
    
    int main(){
        int ca=1;
        while(scanf("%d",&n)!=EOF){
            int u,v;
            init();
            for(int i=1;i<=n;i++) scanf("%d",&c[i]);
            for(int i=1;i<n;i++){
                scanf("%d%d",&u,&v);
                add_edge(u,v);add_edge(v,u);
            }
            dfs(1);
            printf("Case #%d: %lld
    ",ca++,ans);
        }
        return 0;
    }



  • 相关阅读:
    [LeetCode] Baseball Game
    [Linux] Shell Scripts
    [Linux] 正则表达式与文件格式化处理
    [Linux] 学习bash
    [Linux] vim程序编辑器
    [Linux] 文件与文件系统的压缩打包与备份
    [LeetCode] Reverse Words in a String
    [LeetCode] Reverse Integer
    [国嵌笔记][017][Makefile工程管理]
    [国嵌笔记][016][交叉工具链]
  • 原文地址:https://www.cnblogs.com/zhangxianlong/p/10672513.html
Copyright © 2011-2022 走看看