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

题目大意

给定一个 \(n\) 个点 \(m\) 条边的无向连通图,有重边无自环,初始时每个结点的权值为0。
现在要实现两种操作:
0 u x 表示给结点 \(u\) 的权值加上 \(x\)
1 u 表示询问所有和结点 \(u\) 相邻的点的点权之和(如果有多条边这个点的点权就算多次)。
\(1\leq n\leq 100000,1\leq m\leq 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(q\sqrt 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\n",Query(u));}
        }
    }
    return 0;
}
posted @ 2020-09-04 01:27  AE酱  阅读(132)  评论(0编辑  收藏  举报