HDU 4467 Graph [分块]

4 篇文章 0 订阅

题意

给你一张无向图,每条边有权值,点有0/1的权值,一共q个操作:
1. 修改一个点的权值。
2. 询问边两端的点权值为0 0、0 1、1 1的所有边权值和。

题解

首先暴力一点,我们用三个变量aa、ab、bb表示上述询问的三种情况。询问的时候直接输出答案,修改的时候遍历当前点所有的边,修改三个变量即可,但是这样显然是会TLE的,因此我们利用一个分块的思想,将点集合按照点的度分为两个块,度数大于sqrt(m)的分为一块(称为轻点),小于等于的分为另一块(称为重点)。对于重点,我们维护一个have数组,表示当前这个点周围权值是0或1的点,与其连边的权值和。那么对于重点我们通过have数组就可以快速更新三个结果变量,然后我们只需要对重点相连的重点的have数组维护即可,那么对于轻点,我们只需要按照之前的想法暴力修改三个变量与其相连的重点的have数组即可。

AC代码

#include<stdio.h>
#include<vector>
#include<math.h>
#include<map>
#include<string.h>
#define N 100005
using namespace std;
typedef long long ll;
struct node
{
    ll to;
    ll w;
    node(){}
    node(ll to,ll w)
    {
        this->to=to;
        this->w=w;
    }
};
vector<node>vt[N];
ll a[N],du[N],tot,who[N];
ll have[705][2],nowvt[705][705];
char op[25];
int main()
{
    ll n,m,cas=1;
    while(~scanf("%lld%lld",&n,&m))
    {
        memset(nowvt,0,sizeof(nowvt));
        memset(have,0,sizeof(have));
        ll aa=0,ab=0,bb=0;
        tot=0;
        ll len=(ll)sqrt(1.0*m);
        for(ll i=1;i<=n;i++)
            scanf("%lld",&a[i]),vt[i].clear(),du[i]=0;
        for(ll i=0;i<m;i++)
        {
            ll u,v,w;
            scanf("%lld%lld%lld",&u,&v,&w);
            vt[u].push_back(node(v,w));
            vt[v].push_back(node(u,w));
            if(!a[u]&&!a[v])aa+=w;
            else if(a[u]&&a[v])bb+=w;
            else ab+=w;
            du[u]++;
            du[v]++;
        }
        for(ll i=1;i<=n;i++)
            if(du[i]>len)
                who[i]=++tot;
        for(ll i=1;i<=n;i++)
        {
            if(du[i]<=len)continue;
            for(ll j=0;j<vt[i].size();j++)
            {
                ll to=vt[i][j].to;
                ll w=vt[i][j].w;
                have[who[i]][a[to]]+=w;
                if(du[to]>len)
                    nowvt[who[i]][who[to]]+=w;
            }
        }
        ll q;
        scanf("%lld",&q);
        printf("Case %lld:\n",cas++);
        while(q--)
        {
            scanf("%s",op);
            if(op[0]=='A')
            {
                ll x,y;
                scanf("%lld%lld",&x,&y);
                if(!x&&!y)printf("%lld\n",aa);
                else if(x&&y)printf("%lld\n",bb);
                else printf("%lld\n",ab);
            }
            else 
            {
                ll u;
                scanf("%lld",&u);
                if(du[u]<=len)
                {
                    for(ll i=0;i<vt[u].size();i++)
                    {
                        ll to=vt[u][i].to;
                        ll w=vt[u][i].w;
                        if(du[to]>len)have[who[to]][a[u]]-=w,have[who[to]][a[u]^1]+=w;
                        if(a[u]==0)
                        {
                            if(a[to]==0)aa-=w,ab+=w;
                            else ab-=w,bb+=w;
                        }
                        else
                        {
                            if(a[to]==0)ab-=w,aa+=w;
                            else bb-=w,ab+=w;
                        }
                    }
                }
                else 
                {
                    if(a[u]==0)
                    {
                        ab-=have[who[u]][1];
                        aa-=have[who[u]][0];
                        ab+=have[who[u]][0];
                        bb+=have[who[u]][1];  
                    }
                    else 
                    {
                        ab-=have[who[u]][0];
                        bb-=have[who[u]][1];
                        ab+=have[who[u]][1];
                        aa+=have[who[u]][0];
                    }   
                    for(ll i=1;i<=tot;i++)
                    {
                        have[i][a[u]]-=nowvt[who[u]][i];
                        have[i][a[u]^1]+=nowvt[who[u]][i];
                    }
                }
                a[u]^=1;
            }
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值