zoukankan      html  css  js  c++  java
  • hdu 4467 Graph

    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 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 0、0 1、1 1 ,于是可以用三个变量ans[0]、ans[2]、ans[1],来记录总权值之和,遇到Asksum时直接输出就行了,遇到change时,则进行相关处理,来调整ans的值,最暴力

    的方法就是将与x点相连的每条边和点进行处理,q*m的复杂付,无疑是超时的,我当时也就想到了这里,q肯定是不能优化了,

    那么我们的优化对象就应该放在m上了,我当时有个想法,如果把每个点记录与之相连的点中,分别记录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),这个证明很好证,可以自己去推下。接下来,我采取的方法是,用w0和w1表示度数比它小的权值之和,至于度数比它大,则逐边进行处理。这样复杂度是q*(m)^1/2,可以过。最后,我来说下这样为什么不会对其他点的更新造成影响。对于点x,度数大于它的是逐边处理的,点的信息也得到了更新;度数小于它的,信息则没有更新,那么,当处理到那些度数小的点时,也是按同样的方法在处理,度数大于的点逐边处理,这不就是把以前没有更新的过程给完成了吗?这样,就不会出现需要使用改点时,信息没得到完善。总而言之,就是利用度数形成一种顺序的结构,使其不会混乱。



    # include<stdio.h>
    # include<string.h>
    # include<algorithm>
    typedef __int64 ll;
    
    using namespace std;
    struct node
    {
        int st;
        ll w0,w1;
    } p[100005];
    struct edge
    {
        ll u,v;
        ll w;
    } e[100005],ed;
    int color[100005],deg[100005];
    ll ans[3];
    bool cmp(edge x,edge y)
    {
        if (x.u>y.u) return true;
        else return false;
    }
    int main()
    {
        int n,m,i,Case;
        ll u,v,w;
        char s[10];
        Case=1;
        while (scanf("%d%d",&n,&m)!=EOF)
        {
            memset(deg,0,sizeof(deg));
            for (i=1;i<=n;i++) scanf("%d",&color[i]);
            for (i=1;i<=m;i++)
            {
                scanf("%I64d %I64d %I64d",&u,&v,&w);
                e[i].w=w; e[i].v=v; e[i].u=u;
                if (u>v)
                {
                    ll tmp;
                    tmp=e[i].u;
                    e[i].u=e[i].v;
                    e[i].v=tmp;
                }  
            }
            sort(e+1,e+m+1,cmp);  //使u<v,并按u排序,如此可快速判重边
            int k=0;
            for (i=2;i<=m;i++)   //将重边合并
            {
                if (e[i].u==e[i-1].u&&e[i].v==e[i-1].v)
                {
                    k++;
                    e[i-k].w+=e[i].w;
                } else
                {
                    e[i-k].w=e[i].w;
                    e[i-k].u=e[i].u;
                    e[i-k].v=e[i].v;
                }
            }
            m-=k; 
            ans[0]=ans[1]=ans[2]=0;
            for (i=1;i<=m;i++)
            {
                u=e[i].u; v=e[i].v;
                deg[u]++; deg[v]++;  //记录度数
                if (color[u]!=color[v]) ans[2]+=e[i].w;
                else ans[color[u]]+=e[i].w;  //初始化三种情况的权值和
            }
            for (i=1;i<=m;i++)
            {
                u=e[i].u; v=e[i].v;
                if (deg[u]>deg[v])
                {
                    ll tmp;
                    tmp=e[i].u;
                    e[i].u=e[i].v;
                    e[i].v=tmp;
                }
            }
            sort(e+1,e+m+1,cmp);  //使u的度数小于v的度数,并排序,这样可以直接逐边处理
            memset(p,0,sizeof(p));
            for (i=1;i<=m;i++)
            {
                u=e[i].u; v=e[i].v;
                if (p[u].st==0) p[u].st=i;  //记录u节点第一次出现的位置
                if (color[u]) p[v].w1=p[v].w1+e[i].w;
                else p[v].w0=p[v].w0+e[i].w;  //存储点的w0和w1
            }
            int q;
            scanf("%d",&q);
            printf("Case %d:
    ",Case++);
            while (q--)
            {
                int x,y;
                scanf("%s",s);
                if (s[0]=='A')
                {
                    scanf("%d%d",&x,&y);
                    if (x!=y) printf("%I64d
    ",ans[2]);
                    else printf("%I64d
    ",ans[x]);  //这里可以发现为什么ans的表示的巧妙
                }
                else
                {
                    scanf("%d",&x);
                    if (color[x])  //处理小度数的
                    {
                        ans[2]=ans[2]+p[x].w1-p[x].w0;
                        ans[0]=ans[0]+p[x].w0;
                        ans[1]=ans[1]-p[x].w1;
                    }
                    else
                    {
                        ans[2]=ans[2]+p[x].w0-p[x].w1;
                        ans[0]=ans[0]-p[x].w0;
                        ans[1]=ans[1]+p[x].w1;
                    }
                    color[x]=1-color[x];
                    int st=p[x].st;
                    while (st<=m&&e[st].u==x) //大度数的
                    {
                        v=e[st].v;
                        if (color[x]!=color[v])
                        {
                            ans[2]+=e[st].w;
                            ans[1-color[x]]-=e[st].w;
                        }
                        else
                        {
                            ans[color[x]]+=e[st].w;
                            ans[2]-=e[st].w;
                        }
                        if (color[x]==0)
                        {
                            p[v].w0+=e[st].w;
                            p[v].w1-=e[st].w;
                        }
                        else
                        {
                            p[v].w0-=e[st].w;
                            p[v].w1+=e[st].w;
                        }
                        st++;
                    }
                }
            }
        }
        return 0;
    }
    /*
    1、输出long long 变量时需要用%lld
    2、注意在HDOJ上使用%I64d,但是现场赛使用%lld
    */
    























  • 相关阅读:
    《极客时间--算法面试》--二叉树
    《极客时间--算法面试》-哈希表
    《极客时间-算法面试》-堆栈和队列
    《极客时间-算法面试》-数组和链表
    《极客时间-算法面试》
    《极客时间-算法面试》如何计算复杂度
    查找算法
    排序算法
    AI 期刊会议
    《剑指offer》数组中只出现一次的数字
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3209222.html
Copyright © 2011-2022 走看看