您的位置:首页 > 其它

hdu 4467 Graph ( 神奇的思想 降低复杂度 )

2013-07-23 15:55 162 查看

Graph

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 695    Accepted Submission(s): 113

[align=left]Problem Description[/align]
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 xth 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.
 

[align=left]Input[/align]
There are several test cases.

For each test case, the first line contains two integers, n and m (1 ≤ n,m ≤ 105), where n is the number of nodes and m is the number of edges.

The second line consists of n integers, the ith of which represents the color of the ith 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 ≤ 231 - 1) between u and v (u != v).

The next line contains only one integer q (1 ≤ q ≤ 105), the number of operations.

Each of the following q lines describes an operation mentioned before.

Input is terminated by EOF.
 

[align=left]Output[/align]
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.
 

[align=left]Sample Input[/align]

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

 

[align=left]Sample Output[/align]

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

 

[align=left]Source[/align]
2012 Asia Chengdu Regional Contest

题意:给一张无向图,有n个点,m条边,每个点2个状态:0和1,每条边有一个权值w,现在给q个操作。有2种操作,change x表示改变x点的状态。asksum a,b表示查询所有端点为a 和 b的边的权值之和。

思路:这题很容易想到直接暴力解决 但数据量过大 复杂度为O(q*m)  肯定是得TLE的  q不能优化了  那么只能优化m了   暴力的话每次都要对一个点的每条相邻边扫描一遍  太耗时了  于是要想一想能不能每次不遍历所有相邻边呢?

重新分析一下此题:由于每个点只有0 1两个状态,那么答案只有3种情况,用一个数组维护即可。即ans[0]统计边的两端都是0的权值和,ans[1]统计边的两端为1和0的权值总和,ans[2]统计边的两端都是1的权值和。这样只要维护好这个数组,那么对于查询操作,可以O(1)的时间输出。那么关键就在change操作的维护了。

先说一个想法:如果记录每个点与之相连的点颜色分别为0和1 的权值之和,以w0和w1来表示。那么,该点改变时,例如:当当前点由0->1时,ans[0]-w[0],ans[1]+w[1],ans[2]-w[1]+w[0];  这样就是O(1)复杂度了,而且当前点得出结果也是正确的。但是,当处理其他点时,其他点的w0和w1
没有给到更新,这显然是不行的,还需要再做些文章。

说到这里,先说个结论,对于点x,与x相连并且度数大于x的度数的点不会超过 (2m)^(1/2),这个证明很好证,可以自己去推下。如果把与x相连的点分为两类,一类的度数大于他(该类不超过(2m)^(1/2)),另一类度数小于他。对度数小于他的整体操作,度数大于他的逐边操作,那么就能降低复杂度了。接下来,我采取的方法是,用 w[x][0] (与x相连的边的颜色为0)和 w[x][1] (与x相连的边的颜色为1)表示度数比x节点小的权值之和,至于度数比它大,则逐边进行处理,处理时更新w[y][0]、w[y][1]
(因为相对x而言,y是度数大的点,所以可以在对度数比它大的点逐边枚举时也可以更新w的信息,一举三得,因为还降低了复杂度)。这样复杂度就可以降到 q*(m)^1/2 了。

最后,说下这种思想为什么是正确的。对于点x,度数大于它的是逐边处理的,ans可以直接更新;度数小于它的,信息则没有更新,那么,当处理到那些度数小的点时,也是按同样的方法在处理,度数大于的点逐边处理,这不就是把以前没有更新的过程给完成了吗?这样,就不会出现需要使用改点时,信息没得到完善。总而言之,就是利用度数将点分类,然后一部分整体处理,一部分枚举。

感想:这种降低复杂度的思想真的太妙了,值得研究!啥时候能自己想出来就牛逼了。 嘻嘻

哦 对了 这题还要考虑重边的情况,真的好没意思,没办法,现场赛的题目就是各种位置都要坑你一下才行,我对那些哪些卡在重边上的队真的表示惋惜。

代码:

// Exe.Time 1750MS Exe.Memory 6104K hdu暂居第五
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 100005
using namespace std;

int n,m,cxx;
int color[maxn];
int deg[maxn];
long long w[maxn][2],ww[2];
long long ans[3];
char s[50];
struct Node
{
int l,r;
long long val;
} edge[maxn];
int point[maxn];
struct node // 图的邻接表
{
int r,next;
long long val;
}side[maxn];

bool cmp(const Node &x1,const Node &x2)
{
if(x1.l<x2.l) return true ;
else if(x1.l==x2.l) return x1.r<x2.r;
return false ;
}
void addedge(int u,int v,long long cost)
{
cxx++;
side[cxx].r=v;
side[cxx].val=cost;
side[cxx].next=point[u];
point[u]=cxx;
}
int main()
{
int i,j,l,r,q,t=0,tmp,cnt,lc,nc;
long long temp;
while(~scanf("%d%d",&n,&m))
{
t++;
for(i=1; i<=n; i++)
{
scanf("%d",&color[i]);
}
ans[0]=ans[1]=ans[2]=0;
for(i=1; i<=m; i++)
{
scanf("%d%d%I64d",&edge[i].l,&edge[i].r,&edge[i].val);
ans[color[edge[i].l]+color[edge[i].r]]+=edge[i].val; // 更新初始 ans
if(edge[i].l>edge[i].r) // 保证 l<r 方便下面找重边
{
temp=edge[i].l;
edge[i].l=edge[i].r;
edge[i].r=temp;
}
}
sort(edge+1,edge+m+1,cmp);
cnt=1;
for(i=2; i<=m; i++) // 重边合并
{
if(edge[i].l==edge[cnt].l&&edge[i].r==edge[cnt].r)
{
edge[cnt].val+=edge[i].val;
}
else
{
cnt++;
if(i!=cnt)
{
edge[cnt].l=edge[i].l;
edge[cnt].r=edge[i].r;
edge[cnt].val=edge[i].val;
}
}
}
memset(deg,0,sizeof(deg));
for(i=1;i<=cnt;i++) // 记录各点的度数
{
deg[edge[i].l]++;
deg[edge[i].r]++;
}
cxx=0;
memset(point,0,sizeof(point));
memset(w,0,sizeof(w));
for(i=1;i<=cnt;i++) // 对度数比他大的点 建立邻接表
{
l=edge[i].l;
r=edge[i].r;
if(deg[l]<=deg[r])
{
w[r][color[l]]+=edge[i].val; // 度数小的点没有进邻接表 则加入W[]数组内
addedge(l,r,edge[i].val);
}
else
{
w[l][color[r]]+=edge[i].val;
addedge(r,l,edge[i].val);
}
}
printf("Case %d:\n",t);
scanf("%d",&q);
while(q--)
{
scanf("%s",s);
if(s[0]=='A')
{
scanf("%d%d",&l,&r);
printf("%I64d\n",ans[l+r]);
}
else
{
scanf("%d",&r);
lc=color[r];
color[r]=!lc;
ww[0]=ww[1]=0;
for(i=point[r]; i ;i=side[i].next) // 度数大的点进行处理
{
ww[color[side[i].r]]+=side[i].val;
if(lc) // 对度数大的而言 r就是度数小的 需更新信息
{
w[side[i].r][0]+=side[i].val;
w[side[i].r][1]-=side[i].val;
}
else
{
w[side[i].r][0]-=side[i].val;
w[side[i].r][1]+=side[i].val;
}
}
if(lc) // 一起更新ans
{
ans[0]=ans[0]+ww[0]+w[r][0];
ans[1]=ans[1]-ww[0]+ww[1]-w[r][0]+w[r][1];
ans[2]=ans[2]-ww[1]-w[r][1];
}
else
{
ans[0]=ans[0]-ww[0]-w[r][0];
ans[1]=ans[1]+ww[0]-ww[1]+w[r][0]-w[r][1];
ans[2]=ans[2]+ww[1]+w[r][1];
}
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: