zoukankan      html  css  js  c++  java
  • bzoj 2152(点分治)

    传送门

    题意:

    给你一棵树,每条边有权值,现在要求你求出有多少对点对满足他们之间的权值能够被(3)整除。

    分析:

    因为我们需要维护的是树中链的信息,因此我们不妨使用点分治进行统计。因为我们只需要判断是否能够被(3)整除,因此我们只需要统计出点对之间的权值和(\%3)之后的信息,即我们只需要统计出(\%3=0)(\%3=1)(\%3=2)的信息即可,我们计作(cnt[i])

    对于不在一棵子树上的两点,显然权值(\%3=1)的点和权值(\%3=2)的可以分别被贡献答案,答案为(2*cnt[1]*cnt[2]),同理两个权值(\%3=0)的点也可以贡献答案,因此对于不在一棵子树上的两点的贡献为(cnt[1]*cnt[2]*2+cnt[0]*cnt[0])

    而在同一棵子树上的两点因为会加多到他们(lca)的权值贡献,所以需要剪掉这部分的贡献。

    整体的时间复杂度为:(mathcal{O}(nlogn))

    代码:

    #include <bits/stdc++.h>
    #define maxn 20005
    using namespace std;
    struct Node{
        int to,next,val;
    }q[maxn<<1];
    int head[maxn],cnt=0;
    int siz[maxn],Size,dep[maxn],root,minr,CNT[10],res=0,n;
    bool vis[maxn];
    void init(){
        memset(head,-1, sizeof(head));
        cnt=0;
    }
    void add_edge(int from,int to,int val){
        q[cnt].to=to;
        q[cnt].val=val;
        q[cnt].next=head[from];
        head[from]=cnt++;
    }
    void getroot(int x,int fa){
        siz[x]=1;
        int ret=0;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(to==fa||vis[to]) continue;
            getroot(to,x);
            ret=max(ret,siz[to]);
            siz[x]+=siz[to];
        }
        ret=max(ret,Size-siz[x]);
        if(ret<minr) minr=ret,root=x;
    }
    void getdep(int x,int fa){
        CNT[dep[x]]++;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(to==fa||vis[to]) continue;
            dep[to]=(dep[x]+q[i].val)%3;
            getdep(to,x);
        }
    }
    int cal(int x,int pre){
        dep[x]=pre;
        memset(CNT,0,sizeof(CNT));
        getdep(x,x);
        return CNT[0]*CNT[0]+CNT[1]*CNT[2]*2;
    }
    int dfs(int x){
        res+=cal(x,0);
        vis[x]=1;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(vis[to]) continue;
            res-=cal(to,q[i].val);
            minr=n,Size=siz[to];
            getroot(to,-1);
            dfs(root);
        }
    }
    int main()
    {
        scanf("%d",&n);
        init();
        for(int i=1;i<n;i++){
            int from,to,val;
            scanf("%d%d%d",&from,&to,&val);
            add_edge(from,to,val%3);
            add_edge(to,from,val%3);
        }
        Size=minr=n;
        getroot(1,-1);
        dfs(root);
        int tmp=n*n;
        int gcd=__gcd(tmp,res);
        printf("%d/%d
    ",res/gcd,tmp/gcd);
        return 0;
    }
    
    
  • 相关阅读:
    使用Index()+Match()函数实现更为灵活的VLookUp()
    Hexo搭建博客笔记
    Jenkins自动化部署项目
    Ubuntu安装docker
    Ubuntu的简单使用
    ansible之Ad-Hoc
    redis的集群
    redis的主从复制和哨兵
    redis的持久化存储
    redis数据库基础
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11379066.html
Copyright © 2011-2022 走看看