• [图论分块] HDU 4858 项目管理


    题目大意

    给定一个 (n) 个点 (m) 条边的无向连通图,有重边无自环,初始时每个结点的权值为0。
    现在要实现两种操作:
    0 u x 表示给结点 (u) 的权值加上 (x)
    1 u 表示询问所有和结点 (u) 相邻的点的点权之和(如果有多条边这个点的点权就算多次)。
    (1leq nleq 100000,1leq mleq n+10)

    题解

    每一次操作对和当前点相邻的所有点都有影响,这是经典的图论分块问题。
    对于一个结点 (u),如果 (degree[u]leq sqrt {2m}),我们把它标记为轻点,如果 (degree[u]>sqrt {2m}),我们把它标记为重点。
    那么有如下结论:
    1.一个轻点最多只邻接到 (sqrt {2m}) 个结点。
    2.一个重点只邻接到不超过 (sqrt {2m}) 个重点。假设存在一个点 (u) 邻接到超过 (sqrt {2m}) 个重点,那么点 (u) 的度数加上 (u) 邻接到的重点的度数至少为 (2m+sqrt{2m}>2m),由握手定理,(m) 条边的无向图的度数为 (2m),矛盾。

    (u) 邻接到的所有点的点权之和为 (sum[u])
    那么对于修改操作,若 (u) 是轻点,则同时也要修改 (u) 邻接到的所有点的 (sum);若 (u) 是重点,则只修改它邻接到的重点的 (sum)(等于说重点只需向轻点连单向边)。
    对于询问操作,若 (u) 是重点,因为重点的 (sum) 在之前是肯定被更新过的,所以直接输出;若 (u) 是轻点,那么如果 (u) 邻接到的一个重点更新了,并不会去更新 (sum[u]) ,所以需要暴力遍历 (u) 邻接到的所有点来计算 (sum[u])

    因为对一个点进行一次操作最多只需遍历 (sqrt {2m}) 个结点,那么 (q) 次操作,时间复杂度 (O(qsqrt m))

    Code

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <cstdio>
    #include <vector>
    #include <cmath>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    template<typename elemType>
    inline void Read(elemType &T){
        elemType X=0,w=0; char ch=0;
        while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
        while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        T=(w?-X:X);
    }
    
    struct Graph{
        struct edge{int Next,to;};
        edge G[300010];
        int head[100010];
        int cnt;
    
        Graph():cnt(2){}
        void clear(int node_num=0){
            cnt=2;
            if(node_num==0) memset(head,0,sizeof(head));
            else fill(head,head+node_num+5,0);
        }
        void add_edge(int u,int v){
            G[cnt].to=v;
            G[cnt].Next=head[u];
            head[u]=cnt++;
        }
    };
    
    Graph G;
    struct EDGE{int u,v;}E[101000];
    int val[100010],sum[100010],degree[100010];
    int T,N,M,Q,block;
    
    void Update(int u,int x){
        val[u]+=x;
        for(int i=G.head[u];i;i=G.G[i].Next){
            int v=G.G[i].to;
            sum[v]+=x;
        }
    }
    
    int Query(int u){
        if(degree[u]>block) return sum[u];
        int Res=0;
        for(int i=G.head[u];i;i=G.G[i].Next){
            int v=G.G[i].to;
            Res+=val[v];
        }
        return Res;
    }
    
    int main(){
        Read(T);
        while(T--){
            Read(N);Read(M);
            G.clear(N);
            fill(val+1,val+N+1,0);
            fill(sum+1,sum+N+1,0);
            fill(degree+1,degree+N+1,0);
            block=sqrt(2*M);
            for(RG i=1;i<=M;++i){
                Read(E[i].u);Read(E[i].v);
                ++degree[E[i].u];++degree[E[i].v];
            }
            for(RG i=1;i<=M;++i){
                int u=E[i].u,v=E[i].v;
                if(degree[u]<=block || degree[v]>block)
                    G.add_edge(u,v);
                if(degree[v]<=block || degree[u]>block)
                    G.add_edge(v,u);
            }
            Read(Q);
            while(Q--){
                int opt,u,x;
                Read(opt);
                if(opt==0){Read(u);Read(x);Update(u,x);}
                else{Read(u);printf("%d
    ",Query(u));}
            }
        }
        return 0;
    }
    
  • 相关阅读:
    解决博客园中代码着色问题
    使用OutputDebugString输出调试信息
    Android编程小实验
    在安卓3.0以下版本使用Fragment的注意事项
    开源中国客户端源码阅读笔记
    C++基础
    IO流
    OAuth笔记
    反射
    链表
  • 原文地址:https://www.cnblogs.com/AEMShana/p/13611471.html
Copyright © 2020-2023  润新知