HDOJ 4467 Graph 重轻点分块


重/轻点分块

  • 重点: 与该点相关的边大于 sqrt(m)
  • 轻点: 其他的点

如果不分块该怎么做:

  1. 用一个ans[3]的数组记录三种情况的答案,对每个询问 O(1) 直接输出.
  2. 对每个点用一个sum[2]的数组记录,和这个点相关的对面是0的边的权值,
    和对面是1的边的权值.

    对于x的change操作:

    先更新答案ans:

    ans[color[x]+0]-sum[0] && ans[color[x]+1]-sum[1]

    ans[color[x]^1+0]+sum[0] && ans[color[x]^1+1]+sum[1]

    再更新周围点的sum:

    y->sum[x.color]-=cost(x,y)

    y->sum[x.color^1]+=cost(x,y)

这种作法的主要问题是,对于那种边很多的点,会超时.

重/轻点分块的作用:

  • 重点的个数 : 重点的个数不会超过 2*sqrt(m),因为2*sqrt(m)*sqrt(m)/2=m

重点是边比较多的那些点,为了避免频繁的更新,我们让重点维护sum值.
对于重点的每个change操作,直接更新ans,并更新重点周围的其他重点(不超过 2sqrt(m) 个)的sum值.

对与每个轻点,直接暴力统计周围(不超过sqrt(m)个)计算自己的sum值,如果遇到重点顺便更新下重点的sum[0/1]数组.

这样整个时间复杂度就可以AC了

PS: 这题有很多的重边,需要预处理一下


Graph

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2655    Accepted Submission(s): 428


Problem Description
P. T. Tigris is a student currently studying graph theory. One day, when he was studying hard, GS appeared around the corner shyly and came up with a problem:
Given a graph with n nodes and m undirected weighted edges, every node having one of two colors, namely black (denoted as 0) and white (denoted as 1), you’re to maintain q operations of either kind:
* Change x: Change the color of x th node. A black node should be changed into white one and vice versa.
* Asksum A B: Find the sum of weight of those edges whose two end points are in color A and B respectively. A and B can be either 0 or 1.
P. T. Tigris doesn’t know how to solve this problem, so he turns to you for help.
 

Input
There are several test cases.
For each test case, the first line contains two integers, n and m (1 ≤ n,m ≤ 10 5), where n is the number of nodes and m is the number of edges.
The second line consists of n integers, the i th of which represents the color of the i th node: 0 for black and 1 for white.
The following m lines represent edges. Each line has three integer u, v and w, indicating there is an edge of weight w (1 ≤ w ≤ 2 31 - 1) between u and v (u != v).
The next line contains only one integer q (1 ≤ q ≤ 10 5), the number of operations.
Each of the following q lines describes an operation mentioned before.
Input is terminated by EOF.
 

Output
For each test case, output several lines.
The first line contains “Case X:”, where X is the test case number (starting from 1).
And then, for each “Asksum” query, output one line containing the desired answer.
 

Sample Input
  
  
4 3 0 0 0 0 1 2 1 2 3 2 3 4 3 4 Asksum 0 0 Change 2 Asksum 0 0 Asksum 0 1 4 3 0 1 0 0 1 2 1 2 3 2 3 4 3 4 Asksum 0 0 Change 3 Asksum 0 0 Asksum 0 1
 

Sample Output
  
  
Case 1: 6 3 3 Case 2: 3 0 4
 

Source
 



/* ***********************************************
Author        :CKboss
Created Time  :2015年09月02日 星期三 08时40分44秒
File Name     :HDOJ4467_2.cpp
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <set>
#include <map>

using namespace std;

const int maxn=110100;
typedef long long int LL;

int n,m,q;

LL ans[3];
int color[maxn];
bool kind[maxn];
LL sum[maxn][2];

struct IE
{
	int a,b;
	LL c;
	bool operator<(const IE ie) const
	{
		if(a!=ie.a) return a<ie.a;
		if(b!=ie.b) return b<ie.b;
		return c<ie.c;
	}
}ie[maxn];

struct Edge
{
	int to,next;
	LL cost;
}edge[maxn*2],edge2[maxn*2];

int Adj[maxn],Size;
int Adj2[maxn],Size2;

void init()
{
	memset(Adj,-1,sizeof(Adj)); Size=0;
	memset(Adj2,-1,sizeof(Adj2)); Size2=0;
}

void Add_Edge(int k,int u,int v,LL c)
{
	if(k==0)
	{
		edge[Size].to=v;
		edge[Size].next=Adj[u];
		edge[Size].cost=c;
		Adj[u]=Size++;
	}
	else if(k==1)
	{	
		edge2[Size2].to=v;
		edge2[Size2].next=Adj2[u];
		edge2[Size2].cost=c;
		Adj2[u]=Size2++;
	}
}

int du[maxn];

int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);

	int cas=1;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init(); 
		memset(ans,0,sizeof(ans));

		for(int i=1;i<=n;i++) 
		{
			scanf("%d",color+i);
			du[i]=0; kind[i]=false; sum[i][0]=sum[i][1]=0;
		}

		for(int i=0;i<m;i++)
		{
			int u,v;LL c;
			scanf("%d%d%lld",&u,&v,&c);
			if(u>v) swap(u,v);
			ie[i].a=u; ie[i].b=v; ie[i].c=c;
		}

		sort(ie,ie+m);

		int tt=0;
		for(int i=1;i<m;i++)
		{
			if(ie[tt].a==ie[i].a&&ie[tt].b==ie[i].b)
			{
				ie[tt].c+=ie[i].c;
			}
			else
			{
				tt++;
				ie[tt]=ie[i];
			}
		}
		m=tt+1;

		for(int i=0;i<m;i++)
		{
			int u,v; LL c;
			u=ie[i].a; v=ie[i].b; c=ie[i].c;
			ans[color[u]+color[v]]+=c;
			du[u]++; du[v]++;
			Add_Edge(0,u,v,c); Add_Edge(0,v,u,c);
		}

		/***************REBUILD****************************/

		int WP=round(sqrt(m));
		for(int i=1;i<=n;i++) if(du[i]>WP) kind[i]=true;

		for(int u=1;u<=n;u++)
		{
			if(kind[u]==true)
			{
				for(int i=Adj[u];~i;i=edge[i].next)
				{
					int v=edge[i].to;
					LL c=edge[i].cost;

					if(kind[v]==true) { Add_Edge(1,u,v,c); }

					if(color[v]==0)
					{
						sum[u][0]+=c;
					}
					else if(color[v]==1)
					{
						sum[u][1]+=c;
					}
				}
			}
		}

		/********************SOLVE*************************/

		scanf("%d",&q);

		printf("Case %d:\n",cas++);

		char cmd[10];
		int x,y;

		while(q--)
		{
			scanf("%s",cmd);
			if(cmd[0]=='A')
			{
				scanf("%d%d",&x,&y);
				printf("%lld\n",ans[x+y]);
			}
			else if(cmd[0]=='C')
			{
				scanf("%d",&x);
				int c=color[x];

				if(kind[x]==true) /// weight point
				{
					ans[c+0]-=sum[x][0]; ans[c+1]-=sum[x][1];
					ans[(c^1)+0]+=sum[x][0]; ans[(c^1)+1]+=sum[x][1];

					for(int i=Adj2[x];~i;i=edge2[i].next)
					{
						int v=edge2[i].to;
						LL cost=edge2[i].cost;

						sum[v][c]-=cost; sum[v][c^1]+=cost;
					}
				}
				else  /// light point
				{
					LL s[2]={0,0};
					for(int i=Adj[x];~i;i=edge[i].next)
					{
						int v=edge[i].to;
						LL cost=edge[i].cost;

						if(color[v]==0) s[0]+=cost;
						else if(color[v]==1) s[1]+=cost;

						if(kind[v]==true)
						{
							sum[v][c]-=cost; sum[v][c^1]+=cost;
						}
					}

					ans[c+0]-=s[0]; ans[c+1]-=s[1];
					ans[(c^1)+0]+=s[0]; ans[(c^1)+1]+=s[1];
				}
				color[x]=c^1;
			}
		}
	}
    
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值