【HDU 4858】 项目管理 【图的分治】

题解:

共有m条边 设k=sqrt(m) 若一个点的度(即与之有边连接点的个数)小于k成为轻点,剩下的(大于等于k)称为重点

对点i来说degree[i]记录点的度

重点的个数 <=2*m/sqrt(m)=2*sqrt(m)

对于一个点i,A[i]记录这个点的值

对于重点i,sum[i]记录其答案,

对于轻点i,sum[i]无任何作用,轻点的答案通过对所有与轻点相连的点的值求和直接得到

题目可分为三个过程

1.构造图

        对点i来说

        如果i点是轻点,则记录所有与他相连的点到point[i]中

        如果i点是重点,则记录所有与他相连的重点到point[i]中

2.更新图

        对点i来说

        如果i点是轻点,则更新point[i]中所有重点,即为更新所有与之相连的重点的sum值

        如果i点是重点,则更新point[i]中所有点,也即为更新所有与之相连的重点的sum值

3.查询点

       对点i来说

       如果i点是轻点,则累加所有与之相连的点的值直接求出解

       如果i点是重点,则直接输出sum[i],因为sum[i]的值已经更新过了(与重点相连的点有两种——轻点与重点,在2.更新图过程中都完成了更新)



稍微计算一下,就能发现此种方法在点多且密集的情况下,比基础方法快了很多

设轻点个数为N轻,重点个数为N重,轻点所相连点的个数为SIZE轻,重点所相连的个数为SIZE重,重点所相连的重点个数为SIZE重重(重点的个数 <=2*m/sqrt(m)=2*sqrt(m)=2*k=2*SIZE轻)

 image

 忽略N轻与N重 

 

故比较SIZE重与SIZE轻+SIZE重重(SIZE重重<=2*k约等于2*SIZE轻)

              左式=m             右式=3*sqrt(m)

在点多且边密集的图中,显然改进方法的速度有了很大的提升

#include<cmath>  
#include<cstdio>  
#include<cstring>  
#include<vector>  
using namespace std;  
const int MAX=100105;  
struct node  
{  
    int u,v;  
}edge[MAX];;  
int k;  
vector<int>point[MAX];  
int A[MAX],sum[MAX],degree[MAX];  
void update(int x,int d)  
{  
    int i;  
    A[x]+=d;  
    for(i=0;i<point[x].size();i++)  
        if(degree[point[x][i]]>=k)  
            sum[point[x][i]]+=d;  
}  
int query(int x)  
{  
    if(degree[x]<k)  
    {  
        int ans=0;  
        for(int i=0;i<point[x].size();i++)  
            ans+=A[point[x][i]];  
        return ans;  
    }  
    else  
        return sum[x];  
}  
int main()  
{  
    int T,n,m,i,u,v,Q,cmd,a,b;  
    scanf("%d",&T);  
    while(T--)  
    {  
        scanf("%d%d",&n,&m);  
        memset(A,0,sizeof(A));  
        memset(degree,0,sizeof(degree));  
        memset(sum,0,sizeof(sum));  
        for(i=1;i<=n;i++)  
            point[i].clear();  
        for(i=1;i<=m;i++)  
        {  
            scanf("%d%d",&edge[i].u,&edge[i].v);  
            degree[edge[i].u]++;  
            degree[edge[i].v]++;  
        }  
        k=sqrt(m);  
        for(i=1;i<=m;i++)  
        {  
            u=edge[i].u;  
            v=edge[i].v;  
            if(degree[u]<k)  
                point[u].push_back(v);  
            else if(degree[v]>=k)  
                point[u].push_back(v);  
            if(degree[v]<k)  
                point[v].push_back(u);  
            else if(degree[u]>=k)  
                point[v].push_back(u);  
        }  
        scanf("%d",&Q);  
        while(Q--)  
        {  
            scanf("%d",&cmd);  
            if(cmd==0)  
            {  
                scanf("%d%d",&a,&b);  
                update(a,b);  
            }  
            else  
            {  
                scanf("%d",&a);  
                printf("%d\n",query(a));  
            }  
        }  
    }  
    return 0;  
}  

                             对点i来说,如果一个点是轻点,则记录所有与他相连的点到point[i]中

                                                  如果一个点是重点,则记录所有与他相连的重点到point[i]中


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值