[la P5031&hdu P3726] Graph and Queries

[la P5031&hdu P3726] Graph and Queries

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

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 [-106, 106].

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 * 104, 0 <= M <= 6 * 104), the number of vertexes in the graph. The next N lines describes the initial weight of each vertex (-106 <= weight[i] <= 106). 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 * 105], and there will be no more than 2 * 105 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

Hint
For the first sample: D 3 -- deletes the 3rd edge in the graph (the remaining edges are (1, 2) and (2, 3)) Q 1 2 -- finds the vertex with the second largest value among all vertexes connected with 1. The answer is 20. Q 2 1 -- finds the vertex with the largest value among all vertexes connected with 2. The answer is 30. D 2 -- deletes the 2nd edge in the graph (the only edge left after this operation is (1, 2)) Q 3 2 -- finds the vertex with the second largest value among all vertexes connected with 3. The answer is 0 (Undefined). C 1 50 -- changes the value of vertex 1 to 50. Q 1 1 -- finds the vertex with the largest value among all vertex connected with 1. The answer is 50. E -- This is the end of the current test case. Four queries have been evaluated, and the answer to this case is (20 + 30 + 0 + 50) / 4 = 25.000. For the second sample, caution about the vertex with same weight: Q 1 1 – the answer is 20 Q 1 2 – the answer is 20 Q 1 3 – the answer is 10.
 
Source

这题很经典,但又有坑——

首先,这题涉及到删边。

删边不方便,这时我们自然会想到删边的反面——加边。

那我们就可以倒着来操作,方便D操作。

但同时,每个点的权值也要从终状态到始状态来变化。

然后,我们就可以用并查集来实现加边操作,就把一条边加上了。

当然,最终状态也存在一些边,刚开始也要连上。

然后,Q操作要求我们查询连通块内第k大,如果k不合法则返回0。

第k大可以用一个平衡树来维护,我用了treap名次树,方便一些。

但是,注意这里的第k大可能有些定义上的模糊,我最后几发就被坑了。

似乎没有人是加了一个“相同点的个数”这个域A掉了的,我也是改用lrj的写法才A了的,这个需要注意一下。

然后第k大不合法,有可能是超出连通块大小,也有可能k<1。

对于C操作,就相当于再某个连通块中先删掉一个点权为w1的点,再加入一个点权为为w2的点,w1,w2就不多说了。

但是无论如何,都要注意,点权的变化顺序是怎样的。

如果就这样写——就T了。

为什么呢?可能会有数据卡“加边”这一个操作。在这里我们需要合并两颗平衡树。

怎么合并?我们采用启发式合并。就是从节点数小的向节点数大的树并。

怎么证明这样操作的时间复杂度是对的?假设树T1有n1个节点,T2有n2个节点,且n1>n2。

那么显然,我们会把T2并到T1里面。复杂度接近于O(n2logn1)。

由于对于原来T2里的每个点,它所在的树的大小至少增大了一倍,所以对于任意节点,它会被移动不超过logn次。

所以总复杂度是O(n(logn)^2)的。

然后。。通过这个题目,学到了treap的启发式合并和删除。。

code:

  1 %:pragma GCC optimize(2)
  2 #include<bits/stdc++.h>
  3 #define LL long long
  4 using namespace std;
  5 const int N=20005,M=60005,Q=400005;
  6 int n,m,q,cas,w[N],fa[N];
  7 char ch; LL ans,cnt; bool vis[M];
  8 struct edg {int x,y;}a[M];
  9 struct opt {char t; int x,k;}o[Q];
 10 class node {
 11     private:
 12     public:
 13         int v,k,s; node* ch[2];
 14         node() {ch[0]=ch[1]=0;}
 15         inline void newnode(node* &cu,int v) {
 16             cu=new node,cu->v=v,cu->k=rand(),cu->s=1;
 17         }
 18         inline void update(node* &cu) {
 19             cu->s=1;
 20             if (cu->ch[0]!=0) cu->s+=cu->ch[0]->s;
 21             if (cu->ch[1]!=0) cu->s+=cu->ch[1]->s;
 22         }
 23         inline void rotate(node* &cu,bool dr) {
 24             node* tmp=cu->ch[dr^1]->ch[dr];
 25             cu->ch[dr^1]->ch[dr]=cu;
 26             cu=cu->ch[dr^1];
 27             cu->ch[dr]->ch[dr^1]=tmp;
 28             update(cu->ch[dr]);
 29             update(cu);
 30         }
 31         inline void insert(node* &cu,int v) {
 32             if (cu==0) {newnode(cu,v); return;}
 33             bool p=v<=cu->v;
 34             insert(cu->ch[p],v);
 35             if (cu->ch[p]->k<cu->k) rotate(cu,p^1);
 36             else update(cu);
 37         }
 38         inline void remove(node* &cu,int v) {
 39             if (cu==0) return;
 40             if (v==cu->v) {
 41                 if (cu->ch[0]==0&&cu->ch[1]==0) {cu=0; return;}
 42                 if (cu->ch[0]==0||cu->ch[1]==0) {
 43                     cu=cu->ch[1]==0?cu->ch[0]:cu->ch[1]; return;
 44                 }
 45                 bool p=cu->ch[0]->k>cu->ch[1]->k;
 46                 rotate(cu,p^1);
 47                 remove(cu->ch[p^1],v);
 48                 update(cu);
 49                 return;
 50             }
 51             int p=v<=cu->v;
 52             remove(cu->ch[p],v);
 53             update(cu);
 54         }
 55         inline int kth(node* cu,int k) {
 56             if (k>cu->s||k<1||cu==0) return 0;
 57             int s=(cu->ch[0]!=0)?cu->ch[0]->s:0;
 58             if (k==s+1) return cu->v;
 59             if (k<s+1) return kth(cu->ch[0],k);
 60             else return kth(cu->ch[1],k-s-1);
 61         }
 62         inline void merge(node* &major,node* &minor) {
 63             insert(major,minor->v);
 64             if (minor->ch[0]!=0) merge(major,minor->ch[0]);
 65             if (minor->ch[1]!=0) merge(major,minor->ch[1]);
 66             delete(minor),minor=0;
 67         }
 68         inline void clear(node* &cu) {
 69             if (cu->ch[0]!=0) clear(cu->ch[0]);
 70             if (cu->ch[1]!=0) clear(cu->ch[1]);
 71             delete(cu),cu=0;
 72         }
 73 }t,*root[N];
 74 inline int read() {
 75     int x=0,f=1; ch=getchar();
 76     while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
 77     while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
 78     return x*f;
 79 }
 80 inline char readch() {
 81     while (ch<'A'||ch>'Z') ch=getchar();
 82     return ch;
 83 }
 84 inline int get(int x) {
 85     return fa[x]==x?x:fa[x]=get(fa[x]);
 86 }
 87 int main() {
 88     srand(2333),cas=0;
 89     while (scanf("%d%d",&n,&m)!=EOF&&n) {
 90         ans=cnt=q=0,memset(vis,0,sizeof vis);
 91         for (int i=1,x; i<=n; i++) w[i]=read();
 92         for (int i=1; i<=m; i++) a[i].x=read(),a[i].y=read();
 93         for ( ; ; ) {
 94             o[++q].t=readch();
 95             if (o[q].t=='E') break;
 96             o[q].x=read();
 97             if (o[q].t!='D') o[q].k=read();
 98         }
 99         for (int i=1,v; i<q; i++) {
100             if (o[i].t=='D') vis[o[i].x]=1;
101             if (o[i].t=='C') swap(w[o[i].x],o[i].k);
102         }
103         for (int i=1; i<=n; i++) {
104             fa[i]=i; if (root[i]!=0) t.clear(root[i]);
105         }
106         for (int i=1,x,y; i<=m; i++) if (!vis[i]) {
107             x=get(a[i].x),y=get(a[i].y);
108             if (x==y) continue; else fa[x]=y;
109         }
110         for (int i=1; i<=n; i++) t.insert(root[get(i)],w[i]);
111         for (int i=q,x,y; i>=1; i--) {
112             if (o[i].t=='D') {
113                 x=get(a[o[i].x].x),y=get(a[o[i].x].y);
114                 if (x==y) continue;
115                 if (root[x]->s>root[y]->s) fa[y]=x,t.merge(root[x],root[y]);
116                 else fa[x]=y,t.merge(root[y],root[x]);
117             }else
118             if (o[i].t=='Q'){
119                 cnt++,ans+=(LL)t.kth(root[get(o[i].x)],o[i].k);
120             }
121             else
122             if (o[i].t=='C') {
123                 t.remove(root[x=get(o[i].x)],w[o[i].x]);
124                 t.insert(root[x],o[i].k);
125                 w[o[i].x]=o[i].k;
126             }
127         }
128         printf("Case %d: %.6lf\n",++cas,1.0*ans/cnt);
129     }
130     return 0;
131 }
View Code

转载于:https://www.cnblogs.com/whc200305/p/7759781.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值