Hdu 3726 Graph and Queries(并查集+平衡树+启发式合并)

题目链接

Graph and Queries

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


Problem Description
You are given an undirected graph with N vertexes and M edges. Every vertex in this graph has an integer value assigned to it at the beginning. You're also given a sequence of operations and you need to process them as requested. Here's a list of the possible operations that you might encounter:
1)  Deletes an edge from the graph.
The format is [D X], where X is an integer from 1 to M, indicating the ID of the edge that you should delete. It is guaranteed that no edge will be deleted more than once.
2)  Queries the weight of the vertex with K-th maximum value among all vertexes currently connected with vertex X (including X itself).
The format is [Q X K], where X is an integer from 1 to N, indicating the id of the vertex, and you may assume that K will always fit into a 32-bit signed integer. In case K is illegal, the value for that query will be considered as undefined, and you should return 0 as the answer to that query.
3)  Changes the weight of a vertex.
The format is [C X V], where X is an integer from 1 to N, and V is an integer within the range [-10 6, 10 6].

The operations end with one single character, E, which indicates that the current case has ended.
For simplicity, you only need to output one real number - the average answer of all queries.

 

Input
There are multiple test cases in the input file. Each case starts with two integers N and M (1 <= N <= 2 * 10 4, 0 <= M <= 6 * 10 4), the number of vertexes in the graph. The next N lines describes the initial weight of each vertex (-106 <= weight[i] <= 10 6). The next part of each test case describes the edges in the graph at the beginning. Vertexes are numbered from 1 to N. The last part of each test case describes the operations to be performed on the graph. It is guaranteed that the number of query operations [Q X K] in each case will be in the range [1, 2 * 10 5], and there will be no more than 2 * 10 5 operations that change the values of the vertexes [C X V].

There will be a blank line between two successive cases. A case with N = 0, M = 0 indicates the end of the input file and this case should not be processed by your program.

 

Output
For each test case, output one real number – the average answer of all queries, in the format as indicated in the sample output. Please note that the result is rounded to six decimal places.
 

Sample Input
  
  
3 3 10 20 30 1 2 2 3 1 3 D 3 Q 1 2 Q 2 1 D 2 Q 3 2 C 1 50 Q 1 1 E 3 3 10 20 20 1 2 2 3 1 3 Q 1 1 Q 1 2 Q 1 3 E 0 0
 

Sample Output
 
 
Case 1: 25.000000 Case 2: 16.666667

题意:n个结点,m条边的无向图,每个节点有一个整数权值。

3种操作:

D X,删除id为X的边。输入保证每条边至多被删除一次。

Q X k , 计算与结点X联通的结点中(包括X),第K大的权值。如果不存在,输出0。

C X V,把结点X的权值改为V

输出所有询问的平均值。

题解:离线操作,我们把操作倒过来,把删边操作转换为加边。由于要询问第K大,所以我们用平衡树维护一个联通块中的点。当我们要合并两个联通块时,将结点数小的平衡树中的点加入另一个平衡树,删除结点数小的平衡树,这样的合并称为启发式合并。我们来分析一下复杂度,由于1个点合并以后,它所在的树的范围至少增大一倍。所以这个点最多合并logn次,每次合并的复杂度为logn,所以合并的总复杂度为O(n*logn*logn)。单次询问和修改的复杂度为logn。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<string.h>
#include<vector>
#define nn 21000
#define mod 100003
typedef long long LL;
typedef unsigned long long LLU;
const double eps=1e-8;
using namespace std;
int n,m;
int a[nn];
struct node
{
    char c;
    int x,k;
}ask[nn*30];
int u[nn*3],v[nn*3];
bool use[nn*3];
int fa[nn];
struct node1
{
    int val;
    int key;
    int num;
    int sum;
    node1* son[2];
};
node1* Tree[nn];
void update(node1* id)
{
    if(id==NULL)
        return ;
    id->sum=id->num;
    if(id->son[0]!=NULL)
        id->sum+=id->son[0]->sum;
    if(id->son[1]!=NULL)
        id->sum+=id->son[1]->sum;
}
void Rotate(node1* &id,int d)
{
    node1* tem=id->son[d];
    id->son[d]=tem->son[d^1];
    tem->son[d^1]=id;
    update(id);
    update(tem);
    id=tem;
}
void Insert(node1* &id,int val)
{
    if(id==NULL)
    {
        id=new node1;
        id->val=val;
        id->key=rand()*rand();
        id->num=id->sum=1;
        id->son[0]=id->son[1]=NULL;
        return ;
    }
    if(id->val==val)
    {
        id->num++;
        id->sum++;
    }
    else if(val<id->val)
    {
        Insert(id->son[0],val);
        update(id);
        if(id->son[0]->key<id->key)
        {
            Rotate(id,0);
        }
    }
    else
    {
        Insert(id->son[1],val);
        update(id);
        if(id->son[1]->key<id->key)
        {
            Rotate(id,1);
        }
    }
}
void Remove(node1* &id,int val)
{
    if(id==NULL)
        return ;
    if(id->val==val)
    {
        if(id->num==1)
        {
            if(id->son[0]==NULL)
            {
                node1* tem=id;
                id=id->son[1];
                delete tem;
            }
            else if(id->son[1]==NULL)
            {
                node1* tem=id;
                id=id->son[0];
                delete tem;
            }
            else
            {
                if(id->son[0]->key<id->son[1]->key)
                {
                    Rotate(id,0);
                    Remove(id->son[1],val);
                }
                else
                {
                    Rotate(id,1);
                    Remove(id->son[0],val);
                }
            }
        }
        else
            id->num--;
    }
    else if(id->val>val)
        Remove(id->son[0],val);
    else
        Remove(id->son[1],val);
    update(id);
}
int Kth(node1* id,int k)
{
    if(id==NULL||k<=0||k>id->sum)
        return 0;
    int tem=0;
    if(id->son[1]!=NULL)
        tem+=id->son[1]->sum;
    if(tem>=k)
    {
        return Kth(id->son[1],k);
    }
    else if(tem+id->num>=k)
    {
        return id->val;
    }
    else
        return Kth(id->son[0],k-tem-id->num);
}
int findfa(int x)
{
    if(fa[x]==x)
        return x;
    return fa[x]=findfa(fa[x]);
}
void Union(node1* &id,int y)
{
    if(id==NULL)
        return ;
    Union(id->son[0],y);
    Union(id->son[1],y);
    for(int i=0;i<id->num;i++)
    {
        Insert(Tree[y],id->val);
    }
    delete id;
    id=NULL;
}
void Clear(node1* &id)
{
    if(id==NULL)
        return ;
    Clear(id->son[0]);
    Clear(id->son[1]);
    delete id;
    id=NULL;
}
int main()
{
    int i;
    char s[5];
    int cas=1;
    while(scanf("%d%d",&n,&m)&&(n+m))
    {
        memset(use,false,sizeof(use));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&u[i],&v[i]);
        }
        int la=0;
        while(1)
        {
            scanf("%s",s);
            if(s[0]=='E')
                break;
            la++;
            ask[la].c=s[0];
            if(s[0]=='D')
            {
                scanf("%d",&ask[la].x);
                use[ask[la].x]=true;
                ask[la].k=-1;
            }
            else
            {
                scanf("%d%d",&ask[la].x,&ask[la].k);
                if(s[0]=='C')
                {
                    int tem=a[ask[la].x];
                    a[ask[la].x]=ask[la].k;
                    ask[la].k=tem;
                }
            }
        }
        for(i=1;i<=n;i++)
        {
            fa[i]=i;
            Tree[i]=NULL;
            Insert(Tree[i],a[i]);
        }
        for(i=1;i<=m;i++)
        {
            if(!use[i])
            {
                int fx=findfa(u[i]);
                int fy=findfa(v[i]);
                if(fx!=fy)
                {
                    if(Tree[fx]->sum<Tree[fy]->sum)
                    {
                        fa[fx]=fy;
                        Union(Tree[fx],fy);
                    }
                    else
                    {
                        fa[fy]=fx;
                        Union(Tree[fy],fx);
                    }
                }
            }
        }
        double ans=0;
        int q=0;
        for(i=la;i>=1;i--)
        {
            if(ask[i].c=='D')
            {
                int fx=findfa(u[ask[i].x]);
                int fy=findfa(v[ask[i].x]);
                if(fx!=fy)
                {
                    if(Tree[fx]->sum<Tree[fy]->sum)
                    {
                        fa[fx]=fy;
                        Union(Tree[fx],fy);
                    }
                    else
                    {
                        fa[fy]=fx;
                        Union(Tree[fy],fx);
                    }
                }
            }
            else if(ask[i].c=='Q')
            {
                q++;
                ans+=Kth(Tree[findfa(ask[i].x)],ask[i].k);
            }
            else
            {
                int ix=findfa(ask[i].x);
                Remove(Tree[ix],a[ask[i].x]);
                a[ask[i].x]=ask[i].k;
                Insert(Tree[ix],ask[i].k);
            }
        }
        for(i=1;i<=n;i++)
        {
            Clear(Tree[i]);
        }
        printf("Case %d: %.6lf\n",cas++,double(ans)/q);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值