- 传送门 -
http://acm.hdu.edu.cn/showproblem.php?pid=3726
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4170 Accepted Submission(s): 980
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:
- 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. - 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. - 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
PS:
1. 注释掉的部分是使用并查集的方法~
2. 因为询问的是第 k 大节点, 本着能不计算就不计算的原则, 我维护了一个左大右小的二叉树...
- 代码 -
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M = 6e4 + 50;
const int N = 2e4 + 50;
const int maxn = 4e5 + 50;
char qch[maxn][5];
int qx[maxn], qy[maxn]; //记录询问
int bu[M], bv[M], bflag[M], bpre[N]; //记录每一条边
int key[N], size[N], c[N][2], f[N]; //记录树上节点
int v[maxn], head[N], NEXT[maxn]; //记录节点权值的链表
int rt, n, m, sz, cnt, cas, tr, k;
double ans;
void read(int &x) {
x = 0; int f = 1; char ch = getchar();
while (ch > '9' || ch < '0') { if (ch == '-') f= -1; ch = getchar(); }
while (ch <= '9' && ch >= '0') { x = x*10 + ch - '0'; ch = getchar(); }
x *= f;
}
void Init() {
// memset(bpre, 0, sizeof bpre);
memset(head, 0, sizeof head);
k = rt = sz = cnt = 0;
ans = 0;
}
void Pushup(int x) {
if (x) size[x] = size[c[x][0]] + size[c[x][1]] + 1;
}
void rotate(int x) {
int y = f[x], z = f[y];
int a = c[y][1] == x, b = a^1;
if (z) c[z][c[z][1] == y] = x;
f[x] = z, f[y] = x, f[c[x][b]] = y;
c[y][a] = c[x][b], c[x][b] = y;
Pushup(y), Pushup(x);
}
void Splay(int x, int g) {
if (x != g)
while (f[x] != g)
rotate(x);
}
void New_node(int &t, int i, int fa, int val) {
t = i;
key[t] = val;
size[t] = 1;
f[t] = fa;
c[t][0] = c[t][1] = 0;
}
void Add_node(int a, int x) {
v[++ sz] = x;
NEXT[sz] = head[a];
head[a] = sz;
}
void Insert(int x) {
int t = rt;
if (!t) {
c[x][0] = c[x][1] = 0;
f[x] = 0;
size[x] = 1;
rt = x;
return;
}
while (c[t][key[t] > key[x]])
t = c[t][key[t] > key[x]];
New_node(c[t][key[t] > key[x]], x, t, key[x]);
Splay(c[t][key[t] > key[x]], 0);
rt = x;
}
void Travel(int x) {
if (!x) return;
Travel(c[x][0]);
Travel(c[x][1]);
Insert(x);
//结合Insert函数来看, 我们会遍历一棵树将它的每一个节点都插入另一棵树实现合并
//插入操作是会把插入节点旋到根的(或者被插入的是一棵空树时, 插入的节点成为根)
//所以每一次Insert的rt都是不同的, 要在Insert中改变rt的值
}
int Find(int x) {
while (f[x]) x = f[x];
return x;
// return bpre[x] ? bpre[x] = Find(bpre[x]) : x;
}
void Union(int u, int v) {
int x = Find(u), y = Find(v);
if (x == y) return; //在一棵树上
// bpre[x] = y;
Splay(x, 0), Splay(y, 0);
if (size[x] > size[y])
swap(x, y); //启发式合并
rt = y;
Travel(x); //遍历 + 单点插入
}
int Get_kth(int t, int k) {
if (size[c[t][0]] + 1 == k)
return t;
else if (size[c[t][0]] >= k)
return Get_kth(c[t][0], k);
else
return Get_kth(c[t][1], k - size[c[t][0]] - 1);
}
int Pre(int t) {
return c[t][0] ? Pre(c[t][0]) : t;
}
void Delete(int t) {
Splay(t, 0);
if (!c[t][0] || !c[t][1]) {
rt = c[t][0] + c[t][1];
f[rt] = 0;
return;
}
int y = Pre(c[t][1]);
Splay(y, t);
f[c[t][0]] = y;
c[y][0] = c[t][0];
Pushup(y);
f[y] = 0;
rt = y;
//将rt的后继节点y作为它的右儿子, 再把rt的左儿子作为y的左儿子, 删除rt并维护
}
int main(){
while (~scanf("%d %d", &n, &m)) {
if ((!n) && (!m)) return 0;
cas ++;
Init();
for (int i = 1, x; i <= n; i++)
read(x), Add_node(i, x);
for (int i = 1; i <= m; i++)
read(bu[i]), read(bv[i]), bflag[i] = 1;
while (scanf("%s", qch[k]) == 1) {
if (qch[k][0] == 'E') break;
if (qch[k][0] == 'D')
read(qx[k]), bflag[qx[k]] = 0;
else if (qch[k][0] == 'Q')
read(qx[k]), read(qy[k]);
else {
read(qx[k]), read(qy[k]);
Add_node(qx[k], qy[k]);
}
k ++;
}
for (int i = 1; i <= n; i ++)
New_node(rt, i, 0, v[head[i]]);
for (int i = 1; i <= m; i ++)
if (bflag[i]) Union(bu[i], bv[i]);
for (int i = k - 1; i >= 0; i --) {
if (qch[i][0] == 'Q') {
Splay(qx[i], 0);
cnt ++;
if (qy[i] > size[qx[i]] || qy[i] <= 0) //非法询问
continue;
ans += key[Get_kth(qx[i], qy[i])];
}
else if (qch[i][0] == 'D') {
int tmp = qx[i];
Union(bu[tmp], bv[tmp]);
}
else {
Delete(qx[i]);
head[qx[i]] = NEXT[head[qx[i]]];
key[qx[i]] = v[head[qx[i]]]; //更改权值
Insert(qx[i]);
}
}
if (cnt == 0) cnt = 1;
printf("Case %d: %.6lf
", cas, ans / cnt);
}
}