Graph
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
这题是很好的题啊,题目意思就是,给一些点,都是0或1,找两边是00 01 11的边权合,这题的关键就是有的边很多,如果我们就用暴力,那么复杂度必然是p*n,其实,想想问题的关键,就是有的点相连的边太多了,这样暴力会超时,比赛的时候,也是这个问题,虽然有点想法,但当时由于时间太短,不能写出来,下来要想了很长时间,我们要把有很多的边标记下,特殊处理,这样,我们不就能省掉很多时间了么?基于,这样的想法,我们,就可以这样做,把超过界限的点,标记成超极点,其他的就是普通点,我们连接超极点和超极点,普通点与普通点。超极点,但是,超极点不连接普通点!对于超极点,我们用一个数组,保存它周围的0和1的点的权和,这样我们就可以用0(1)的时间,快速维护最终的结果,对于普通点,我们就暴力维护就可以了,因为他周围的点少,时间就可以大大的省下来!这样的想法很好,复杂度是多少呢?我们可以发现,如果我们把办限定为sqrt(m),那么超极点的个数一定不会多于2*sqrt(m),这一点很好证明,那么这样,所有点的暴力维护的值都是sqrt(m)了,那么复杂度就是n*sqrt(m),当然,这是可以过了的,这种思想很有用,也很神奇啊,当时,有点影子,但没有时间,来实现了!对于本题一定要注意重边的情况和一定要用--int64,好了,到这里,就可以a了!还有一点小技巧,见代码!
#include#include #include #include #include using namespace std;#define MAXN 100005struct nodeedge{ int s,e; __int64 val;}edge[40*MAXN];struct nodelist{ int next,to,flag;}l[200*MAXN];char str[300];int head[MAXN],p[MAXN],index[MAXN],limit;//判定是否是超极点__int64 sum[3],sumsuper[MAXN][2];void change(int x){ int i,j; int temp=p[x]^1,t; for(j=head[x];j!=-1;j=l[j].next) { i=l[j].to; sum[p[x]+p[i]]-=edge[j].val; sum[temp+p[i]]+=edge[j].val; if((!l[x].flag)&&l[i].flag) { sumsuper[i][p[x]]-=edge[j].val; sumsuper[i][temp]+=edge[j].val; } } if(l[x].flag) { sum[p[x]]-=sumsuper[x][0]; sum[temp]+=sumsuper[x][0]; sum[p[x]+1]-=sumsuper[x][1]; sum[temp+1]+=sumsuper[x][1]; } p[x]=p[x]^1;//取反}void build(int i,int s,int e,__int64 val)//建邻接表{ if(l[s].flag&&(!l[e].flag)) { sumsuper[s][p[e]]+=val; } else { l[i].next=head[s]; l[i].to=e; head[s]=i; }}bool cmp(nodeedge a,nodeedge b){ if(a.s != b.s) return a.s < b.s; else return a.e < b.e;}int main (){ int n,m,i,edgem,m2,asnum,a1,a2,tcase; tcase=1; while(scanf("%d%d",&n,&m)!=EOF) { limit=350;m2=2*m; sum[0]=sum[1]=sum[2]=0; for(i=1;i<=n;i++) { scanf("%d",&p[i]); index[i]=0;head[i]=-1; } for(i=1;i<=m;i++) { scanf("%d%d%I64d",&edge[i].s,&edge[i].e,&edge[i].val); edge[i+m].s=edge[i].e,edge[i+m].e=edge[i].s,edge[i+m].val=edge[i].val;//建反向边 sum[p[edge[i].s]+p[edge[i].e]]+=edge[i].val; } sort(edge+1,edge+m2+1,cmp);//边从1开始计数 edgem=1; for(i=2;i<=m2;i++)//去掉重边 { if((edge[i].s==edge[edgem].s)&&(edge[i].e==edge[edgem].e)) { edge[edgem].val+=edge[i].val; } else{ edgem++; edge[edgem]=edge[i]; } } for(i=1;i<=edgem;i++) { index[edge[i].s]++; } for(i=1;i<=n;i++)//统计超极结点 { if(index[i]>=limit) { l[i].flag=1;//是超极结点 sumsuper[i][0]=sumsuper[i][1]=0; } else l[i].flag=0; } for(i=1;i<=edgem;i++)//建领接表 { build(i,edge[i].s,edge[i].e,edge[i].val); } scanf("%d",&asnum); printf("Case %d:\n",tcase++); while(asnum--) { scanf("%s",str); if(str[0]=='A') { scanf("%d%d",&a1,&a2); printf("%I64d\n",sum[a1+a2]); } else{ scanf("%d",&a1); change(a1); } } } return 0;}