hdu4467 Graph(分段处理)

来源:互联网 发布:淘宝开店保证金30元 编辑:程序博客网 时间:2024/06/05 18:41

Graph

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


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 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.
 

Input
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.
 

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 30 0 0 01 2 12 3 23 4 34Asksum 0 0Change 2Asksum 0 0Asksum 0 14 30 1 0 01 2 12 3 23 4 34Asksum 0 0Change 3Asksum 0 0Asksum 0 1
 

Sample Output
Case 1:633Case 2:304
 


题意:
n个点,m条边,每一个点有颜色(0/1),每一条无向边u,v,w(权值)
有两种操作1.改变第x个节点的颜色(0->1,1->0)   2.输出两头节点是x,y的边的权值和

解析:
这里可以用三个量储存三种边(对于2操作),ze(0,1),zz(0,0),ee(1,1)   ,这样对于第二种操作就只是O(1)的复杂度,剩下的只要维护这三个变量就行了
主要是第一个操作,该边的颜色,同时还要维护三个变量,最普通的就是暴力都扫一遍,这样复杂度就是O(q*m),所以看了大神的解析就是分段来处理。
分成两部分1.轻点(度数小于sqrt(m))  2.重点(度数大于sqrt(m))    这样一操作的复杂度就大约为O(q*sqrt(m)),且重点的个数不会超过2*sqrt(m),(后面证明),大约不超过700个

这样对于重点就要开一个数组node[i][0/1]来记录它周围颜色是0(1)的点到它的边的权值和,并且由于每执行一次1操作就肯定要更新一遍node[][]数组,所以对于重点之间就建立
一张图mymap[][],来更新。对于轻点,就只需要暴力搜索一遍就好了,不过要注意的是在搜索过程中如果碰到重点就要更新node[][]。总而言之,在更新过程中,如果碰到重点就要更新node[][],如果x是轻点,那么暴力搜索时碰到重点更新node[][],如果x是重点,那么碰到轻点,并不需要做什么,只有碰到重点的时候才要更新,又因为重点的度数过大,所以就建立一张重点间的图来更新

证明:
度数为sqrt(m)的原因是 ,设度数大于sqrt(m)的点有cnt个,那么所有重点的度数和一定小于总度数(或重点之间的边数一定小于总边数m) cnt*sqrt(m)<=2*m  ->   cnt<=2*sqrt(m)
而推出来的原因(个人推测):设临界的度数为k,度数大于k的点个数为w,  q*k+q*w<=1e8  ->  k+w<=1e3  并且k,w与m成一定的关系  k*w<=2*m(1e5),所以这样大致推出来是sqrt(m),这也只是我反过来去推的,不知道正不正确

#include<cstdio>#include<cstring>#include<algorithm>#include<map>using namespace std;#define M(a) memset(a,0,sizeof(a))const int MAXN = 1e5+10;typedef long long int ll;int n,m;typedef struct edge{    int u,v;    ll w;    int next;}edge;edge graph[MAXN*2];int color[MAXN],cnt,lim;int indegree[MAXN],head[MAXN];int stand[MAXN],splt;ll mymap[700][700];ll node[700][2];ll zz,ee,ze;map<ll,int> mp;void addEdge(int u,int v,int w){    graph[cnt].u=u;    graph[cnt].v=v;    graph[cnt].w=w;    graph[cnt].next=head[u];    head[u]=cnt++;}void solve(){    for(int i=1;i<=n;i++)    {        if(indegree[i]>lim)            stand[i]=splt++;    }    for(int i=0;i<cnt;i+=2)  //建立重点的相关边的权值和列表,以及重点间的map图    {        int u=graph[i].u;        int v=graph[i].v;        ll w=graph[i].w;        if(color[u]^color[v])   //01            ze+=w;        else if(color[u])  //11            ee+=w;        else    //00            zz+=w;        if(indegree[u]>lim)        {            node[stand[u]][color[v]]+=w;        }        if(indegree[v]>lim)        {            node[stand[v]][color[u]]+=w;        }        if(indegree[u]>lim&&indegree[v]>lim)        {            mymap[stand[u]][stand[v]]+=w;            mymap[stand[v]][stand[u]]+=w;        }    }}void Change(int d){    if(indegree[d]>lim)    {        int u=stand[d];        for(int i=0;i<splt;i++)   //更新重点node        {            if(mymap[u][i])            {                node[i][color[d]]-=mymap[u][i];                node[i][color[d]^1]+=mymap[u][i];            }        }        if(color[d])        {            color[d]=0;            ze-=node[u][0];            ze+=node[u][1];            ee-=node[u][1];   //11减去原来的11            zz+=node[u][0];   //00加上现在的00        }        else        {            color[d]=1;            ze-=node[u][1];            ze+=node[u][0];            ee+=node[u][1];   //11加上现在的11            zz-=node[u][0];   //00减去原来的00        }    }    else    {        for(int i=head[d];i!=-1;i=graph[i].next)        {            int v=graph[i].v;            ll w=graph[i].w;            if(color[d]^color[v])            {                ze-=w;                if(color[v]) ee+=w;                else zz+=w;            }            else if(color[v])            {                ee-=w;                ze+=w;            }            else            {                zz-=w;                ze+=w;            }            if(indegree[v]>lim)     //在遍历轻点时要更新遍历的重点node            {                node[stand[v]][color[d]]-=w;                node[stand[v]][color[d]^1]+=w;            }        }        color[d]^=1;    }}int main(){    int q;    ll cas=0;    while(scanf("%d%d",&n,&m)!=EOF)    {        cas++;        memset(head,-1,sizeof(head));        memset(stand,-1,sizeof(stand));        mp.clear();        cnt=splt=0;        lim=(int)sqrt(1.0*m);        M(node);        M(indegree);        M(mymap);        zz=ze=ee=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&color[i]);        }        int a,b,c;        for(int i=0;i<m;i++)        {            scanf("%d%d%d",&a,&b,&c);            if(a>b) swap(a,b);            ll tmp=(ll)a*100000+b;            if(!mp[tmp])            {                indegree[a]++;                indegree[b]++;                addEdge(a,b,c);                addEdge(b,a,c);                mp[tmp]=cnt-1;            }            else            {                int id=mp[tmp];                graph[id].w+=c;                graph[id^1].w+=c;            }        }        solve();        printf("Case %lld:\n",cas);        scanf("%d",&q);        char str[50];        //printf("Case %lld:\n",cas);        for(int i=0;i<q;i++)        {            scanf("%s",str);            if(strcmp(str,"Asksum")==0)            {                int d,e;                scanf("%d%d",&d,&e);                if(d==e&&d==1)                {                    printf("%lld\n",ee);                }                else if(d==e&&d==0)                {                    printf("%lld\n",zz);                }                else                {                    printf("%lld\n",ze);                }            }            else if(strcmp(str,"Change")==0)            {                int d;                scanf("%d",&d);                Change(d);            }        }            }    return 0;}