hdu 4467 Graph 阀值



Graph

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


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
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   5551  5550  5549  5548  5547 



解题报告:

坑点放在最前面:有重边!!!!!!这个会导致一个点的颜色更改后,与之相邻的某个点的对应信息会被多次更改!!!


怎样使它不超时:
很容易想到,每个点维护一个一维数组sum[2],sum[0]表示与之相邻为白色点的边权和,sum[1]为与之相邻为黑色点的边权和。
对于整个图,维护一个一位数组ans[3],ans[1]代表变得两端分别是白色点的边权和,ans[1]两端一黑一白,ans[2]两端黑。

但是n、m最大1e5,查询次数最多1e5,
这么一来最极端的情况就是 1e5* 1e5=1e10,远远超时

今天看了别人的博客,又学习到了一个新方法:

首先我们要把点分为两类,一种是普通点,一种是超级点,每次更改点的颜色是,把这两种点区别对待,则可在规定时间内完成任务。

普通点:与之相邻的点数小于阀值upcase。

超级点:与之相邻的点数达到阀值upcase。

我们把阀值upcase定位 sqrt(m);

可以证明超级点的数量不超过2*sqrt(m):
若超过,则 边总数会超过 2*sqrt(m)*sqrt(m)/2=m;

假若对于超级点,处理一次发杂度为O(sqrt(m)),对于普通点处理一次复杂度也为O(sqrt(m)),那么总时间是O(query*sqrt(m)+query*sqrt(m))=O(query*sqrt(m))
就可以在规定时间内完成任务。

首先每个点要记录它是什么颜色。
那能不能做到呢?
1.
对于更改的超级点:
我们只用利用他的sum[0]和sum[1]来维护最终结果,并且更改与之相邻不超过2*sqrt(m)个超级点的相关信息。
那么与之相邻的普通点呢?不用管它,不用更改与之相关的信息,为什么呢?看下面。
2.
对于更改的普通点:
我们暴力遍历它的相邻点,来维护ans[];因为它是普通点,所以与之相邻的点不超过sqrt(m)个。
因为是暴力遍历,所以根本不用维护这些普通点的sum值。

这样一来无论更改什么点,时间复杂度都是O(sqrt(m))级别。



#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<climits>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<stack>
typedef long long ll;
using namespace std;
const int maxn=1e5+10;
const int maxm=1e5+10;
int upcase,n,m;
ll ans[3];
struct Node
{
    int cnt;
    ll sum[2];
    int color;


}node[maxn];
struct Edge
{
    int from,to;
    ll w;
    Edge(){}
    Edge(int &from,int &to,ll &w):from(from),to(to),w(w){}
    bool operator<(const Edge b)const
    {
        if(from!=b.from)  return from<b.from;
        return to<b.to;
    }
};
vector<Edge>edges;
vector<int >G[maxn];
vector<ll >W[maxn];
void init()
{
    upcase=sqrt(m+0.5);
    for(int i=1;i<=n;i++)
        G[i].clear(),W[i].clear();
    edges.clear();

    ans[0]=ans[1]=ans[2]=0;
}

char s[12];

void change(int x)
{
    int &cx=node[x].color;
    if(node[x].cnt<upcase)
    {

        for(int i=0;i<G[x].size();i++)
        {
            int &y=G[x][i];
            int &cy=node[y].color;
            ans[cx+cy] -=W[x][i];
            ans[(cx^1)+cy]+=W[x][i];
            node[y].sum[cx]-=W[x][i];
            node[y].sum[cx^1]+=W[x][i];
        }
    }
    else
    {
        ans[cx    +0]-=node[x].sum[0];
        ans[(cx^1)+0]+=node[x].sum[0];
        ans[cx    +1]-=node[x].sum[1];
        ans[(cx^1)+1]+=node[x].sum[1];
        for(int i=0;i<G[x].size();i++)
        {
            int &y=G[x][i];
            int &cy=node[y].color;
            node[y].sum[cx]-=W[x][i];
            node[y].sum[cx^1]+=W[x][i];
        }
    }
    cx^=1;//掉了这句话呀!!!!
}


inline void query()
{
    scanf("%s",s);
    int x,y;
    if(s[0]=='A')
    {
        scanf("%d%d",&x,&y);
        printf("%lld\n",ans[x+y]);
    }
    else
    {
        scanf("%d",&x);
        change(x);
    }
}
bool vis[maxm];
int main()
{
    int x,y,q,kase=0;ll w;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&node[i].color);
            node[i].sum[1]=node[i].sum[0]=0,node[i].cnt=0;
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%lld",&x,&y,&w);
            if(x>y)  swap(x,y);
            edges.push_back(Edge(x,y,w ) );
//            node[x].cnt++,node[y].cnt++; 放这里绝错
            int cx=node[x].color;
            int cy=node[y].color;

            node[x].sum[cy]+=w;
            node[y].sum[cx]+=w;
            ans[cy+cx]+=w;
        }
        sort(edges.begin(),edges.end() );
        memset(vis,0,sizeof vis);
        for(int i=0;i<m;i++)
        {
            Edge & e=edges[i];
            int x=e.from;
            int y=e.to;
            if(i&&x==edges[i-1].from&&y==edges[i-1].to)
            {
                e.w+=edges[i-1].w;
                vis[i-1]=1;
            }
        }
        for(int i=0;i<m;i++)
        {
            if(vis[i])  continue;
            Edge & e=edges[i];
            int x=e.from;
            int y=e.to;
            node[x].cnt++,node[y].cnt++;

        }

        for(int i=0;i<m;i++)
        {
            if(vis[i])  continue;
            Edge & e=edges[i];
            int x=e.from;
            int y=e.to;
            ll w=e.w;

            if(node[x].cnt>=upcase&&node[y].cnt>=upcase)
            {
                W[x].push_back(w);
                G[x].push_back(y);
                W[y].push_back(w);
                G[y].push_back(x);
            }
            if(node[x].cnt<upcase) G[x].push_back(y),W[x].push_back(w);

            if(node[y].cnt<upcase) G[y].push_back(x),W[y].push_back(w);
        }
        scanf("%d",&q);
        printf("Case %d:\n",++kase);
        for(int i=1;i<=q;i++)
        {
            query();
        }

    }




}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值