HDU 4858 项目管理 分块

本文提供了一种解决HDU 4858问题的有效算法,通过将节点分为重点和轻点来优化插入和查询操作的时间复杂度至sqrt(m),并详细解释了实现过程。

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4858

题解:

下面说一个插入查询时间复杂度为sqrt(m)的算法:

对每个点定义两个值:val,sum,val记录自己的特征值,sum记录周边所有点特征值的和。

现在我们把所有的节点分成两类,重点(度数>=sqrt(m)),轻点(度数sqrt(m))。

插入:

轻点更新自己的val,同时更新所有的邻点的sum值

重点更新自己的val,同时只更新相邻重点的sum值(所以重点不需要连边到轻点)

查询:

轻点:暴力周边的所有邻点的val值。

重点:直接输出自己的sum值。

性质:

与重点相邻的重点不超过sqrt(m)个。

与轻点相邻的所有点不超过sqrt(m)个。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<utility>
#include<cmath>
using namespace std;

typedef long long LL;
const int maxn=1e5+10;

vector<int> G[maxn];
pair<int,int> E[maxn];

LL val[maxn],sum[maxn];
int cnt[maxn];
int n,m,q;

void init(){
    memset(val,0,sizeof(val));
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
    memset(E,0,sizeof(E));
    for(int i=1;i<=n;i++) G[i].clear();
}

int main(){
    int tc;
    scanf("%d",&tc);
    while(tc--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=0;i<m;i++){
            int u,v;
            scanf("%d%d",&E[i].first,&E[i].second);
            cnt[E[i].first]++,cnt[E[i].second]++;
        }
        for(int i=0;i<m;i++){
            int u=E[i].first,v=E[i].second;
            if(cnt[u]>cnt[v]) swap(u,v);
            if(cnt[u]<sqrt(m+0.5)){
                G[u].push_back(v);
                if(cnt[v]<sqrt(m+0.5)) G[v].push_back(u);
            }
            else{
                G[u].push_back(v);
                G[v].push_back(u);
            }
        }
        scanf("%d",&q);
        while(q--){
            int cmd; scanf("%d",&cmd);
            if(cmd==0){
                int id,v;
                scanf("%d%d",&id,&v);
                val[id]+=v;
                for(int i=0;i<G[id].size();i++){
                    sum[G[id][i]]+=v;
                }
            }else{
                int id;
                scanf("%d",&id);
                if(cnt[id]>=sqrt(m+0.5)) printf("%lld\n",sum[id]);
                else{
                    LL res=0;
                    for(int i=0;i<G[id].size();i++){
                        res+=val[G[id][i]];
                    }
                    printf("%lld\n",res);
                }
            }
        }
    } 
    return 0;
} 

 

转载于:https://www.cnblogs.com/fenice/p/5553105.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值