名次树还是比较容易理解的。这道题经典主要在于可以用并查集来维护连通的点,并且使用离线算法,倒过来处理,把分开变成了合并!!
PS: 虽然这道题不存在这个问题。但有时,可能remove不存在的值,如果不先find就可能会爆栈。。(因为一直找不到->一直找)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <set>
#include <limits>
using namespace std;
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define REP(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define UREP(i, s, t) for(int (i)=(s);(i)>=(t);--(i))
#define REPOK(i, s, t, o) for(int (i)=(s);(i)<=(t) && (o);++(i))
#define MAXN 100
#define MAXM 10000
#define MOD 10000007
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
typedef long long LL;
const double maxdouble = numeric_limits<double>::max();
const double eps = 1e-10;
const int INF = 0x7FFFFFFF;
//Treap
// 每个节点有两个属性:rank和value
// 按照rank 这是一个堆
// 按照value 是一颗排序二叉树
//节点定义
typedef struct Node {
Node *ch[2];
int r; // rank
int v; // value
int s; // size 名次树中子树结点个数
int cmp(int x) const {
if (x == v) return -1;
return x < v ? 0 : 1;
}
void maintain() {
s = 1;
if (ch[0] != NULL) s += ch[0]->s;
if (ch[1] != NULL) s += ch[1]->s;
}
}TreapNode;
class Treap {
public:
void insert(TreapNode* &o, int x) {
if (o == NULL) {
//o = new TreapNode();
//o->ch[0] = o->ch[1] = NULL;
//o->v = x;
//o->r = rand();
//o->s = 1;
make_node(o, x);
} else {
//int d = o->cmp(x);
int d = o->v > x ? 0 : 1; // 名次数中的v可能重复
insert(o->ch[d], x);
if (o->ch[d]->r > o->r)
rotate(o, d^1);
}
o->maintain();
}
// 两棵子树:先把优先级较高的旋转到根
// 然后在另一棵子树中递归删除x
void remove(TreapNode* &o, int x) {
int d = o->cmp(x);
if (d == -1) {
TreapNode* u = o;
if (o->ch[0] == NULL) {
o = o->ch[1];
delete u;
} else if (o->ch[1] == NULL) {
o = o->ch[0];
delete u;
} else {
if (o->ch[0]->r > o->ch[1]->r) {
d = 1;
} else {
d = 0;
}
rotate(o, d);
remove(o->ch[d], x);
}
} else {
remove(o->ch[d], x);
}
if(o != NULL) o->maintain();
}
// 求第k大的值 0表示不存在
int kth(TreapNode* o, int k) {
if (o == NULL || k <= 0 || k > o->s) return 0;
int s = o->ch[1] == NULL ? 0 : o->ch[1]->s;
if (k == s+1) return o->v;
else if (k <= s) return kth(o->ch[1], k);
else return kth(o->ch[0], k-s-1);
}
void make_node(TreapNode* &o, int x) {
o = new TreapNode();
o->ch[0] = o->ch[1] = NULL;
o->r = rand();
o->s = 1;
o->v = x;
}
void destroy(TreapNode* &o) {
if (o->ch[0] != NULL) destroy(o->ch[0]);
if (o->ch[1] != NULL) destroy(o->ch[1]);
delete o;
o = NULL;
}
private:
// 旋转操作:d=0左转 d=1右转
void rotate(TreapNode* &o, int d) {
TreapNode* k = o->ch[d^1];o->ch[d^1] = k->ch[d];k->ch[d] = o;
// o成为k的子树 而k的另一个子树没发生变化
// 所以先维护o 在维护k
o->maintain();
k->maintain();
o = k;
}
};
struct Command{
char c;
int param1;
int param2;
};
const int maxn = 20000;
const int maxm = 60000;
const int maxc = 600000;
int n, m;
Command cmds[maxc+5];
int from[maxm + 5];
int to[maxm + 5];
bool removed[maxm + 5];
int w[maxn + 5];
//Treap
Treap treap;
TreapNode* root[maxn + 5];
// Union-find
int pa[maxn + 1];
int findset(int x) {
return pa[x] == x ? x : pa[x] = findset(pa[x]);
}
void mergeto(TreapNode* &src, TreapNode* &dst) {
if (src->ch[0] != NULL) mergeto(src->ch[0], dst);
if (src->ch[1] != NULL) mergeto(src->ch[1], dst);
treap.insert(dst, src->v);
delete src;
src = NULL;
}
int main() {
//freopen("input.in", "r", stdin);
char ch;
int cases = 0, x, y;
while(scanf("%d%d",&n,&m) == 2 && (n || m)) {
memset(removed, false, sizeof(removed));
REP(i, 1, n) scanf("%d", &w[i]);
REP(i, 1, m) scanf("%d%d",&from[i], &to[i]);
int cnt = 0;
for(;;) {
for(;;) {
scanf("%c",&ch);
if (isalpha(ch))
break;
}
cmds[cnt].c = ch;
if (ch == 'E') break;
if (ch == 'D') {
scanf("%d",&x);
removed[x] = true;
cmds[cnt].param1 = x;
} else if(ch == 'Q') {
scanf("%d%d",&x, &y);
cmds[cnt].param1 = x;
cmds[cnt].param2 = y;
} else {
scanf("%d%d",&x, &y);
cmds[cnt].param1 = x;
cmds[cnt].param2 = w[x];
w[x] = y;
}
++cnt;
}
REP(i, 1, n) treap.make_node(root[i], w[i]);
REP(i, 1, n) pa[i] = i;
REP(i, 1, m)
if (!removed[i]) {
x = findset(from[i]);
y = findset(to[i]);
if (x != y) {
if (root[x]->s < root[y]->s) {
mergeto(root[x], root[y]);
pa[x] = y;
} else {
mergeto(root[y], root[x]);
pa[y] = x;
}
}
}
LL sum = 0;
int query_cnt = 0;
UREP(i, cnt-1, 0) {
if (cmds[i].c == 'D') {
int e = cmds[i].param1;
int x = findset(from[e]), y = findset(to[e]);
if (x != y) {
if (root[x]->s < root[y]->s) {
mergeto(root[x], root[y]);
pa[x] = y;
} else {
mergeto(root[y], root[x]);
pa[y] = x;
}
}
} else if (cmds[i].c == 'C') {
int x = cmds[i].param1;
int v = cmds[i].param2;
int fa = findset(x);
treap.remove(root[fa], w[x]);
treap.insert(root[fa], v);
w[x] = v;
} else {
int x = findset(cmds[i].param1);
int k = cmds[i].param2;
++query_cnt;
sum += treap.kth(root[x], k);
}
}
printf("Case %d: %.6f\n",++cases, sum/(double)query_cnt);
REP(i, 1, n)
if (root[i] != NULL) treap.destroy(root[i]);
}
return 0;
}
使用真实指针null代替NULL的版本
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <set>
#include <limits>
using namespace std;
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define REP(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define UREP(i, s, t) for(int (i)=(s);(i)>=(t);--(i))
#define REPOK(i, s, t, o) for(int (i)=(s);(i)<=(t) && (o);++(i))
#define MAXN 100
#define MAXM 10000
#define MOD 10000007
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
typedef long long LL;
const double maxdouble = numeric_limits<double>::max();
const double eps = 1e-10;
const int INF = 0x7FFFFFFF;
//Treap
// 每个节点有两个属性:rank和value
// 按照rank 这是一个堆
// 按照value 是一颗排序二叉树
//节点定义
typedef struct Node {
Node *ch[2];
int r; // rank
int v; // value
int s; // size 名次树中子树结点个数
int cmp(int x) const {
if (x == v) return -1;
return x < v ? 0 : 1;
}
void maintain() {
s = ch[0]->s + ch[1]->s + 1;
}
}TreapNode;
class Treap {
public:
Treap() {
null = new TreapNode();
null->ch[0] = null->ch[1] = null;
null->s = 0;}
~Treap(){delete null;}
void insert(TreapNode* &o, int x) {
if (o == null) {
//o = new TreapNode();
//o->ch[0] = o->ch[1] = null;
//o->v = x;
//o->r = rand();
//o->s = 1;
make_node(o, x);
} else {
//int d = o->cmp(x);
int d = o->v > x ? 0 : 1; // 名次数中的v可能重复
insert(o->ch[d], x);
if (o->ch[d]->r > o->r)
rotate(o, d^1);
}
o->maintain();
}
// 两棵子树:先把优先级较高的旋转到根
// 然后在另一棵子树中递归删除x
void remove(TreapNode* &o, int x) {
int d = o->cmp(x);
if (d == -1) {
TreapNode* u = o;
if (o->ch[0] == null) {
o = o->ch[1];
delete u;
} else if (o->ch[1] == null) {
o = o->ch[0];
delete u;
} else {
if (o->ch[0]->r > o->ch[1]->r) {
d = 1;
} else {
d = 0;
}
rotate(o, d);
remove(o->ch[d], x);
}
} else {
remove(o->ch[d], x);
}
if(o != null) o->maintain();
}
// 求第k大的值 0表示不存在
int kth(TreapNode* o, int k) {
if (o == null || k <= 0 || k > o->s) return 0;
int s = o->ch[1] == null ? 0 : o->ch[1]->s;
if (k == s+1) return o->v;
else if (k <= s) return kth(o->ch[1], k);
else return kth(o->ch[0], k-s-1);
}
void make_node(TreapNode* &o, int x) {
o = new TreapNode();
o->ch[0] = o->ch[1] = null;
o->r = rand();
o->s = 1;
o->v = x;
}
void destroy(TreapNode* &o) {
if (o == null) return;
if (o->ch[0] != null) destroy(o->ch[0]);
if (o->ch[1] != null) destroy(o->ch[1]);
delete o;
o = null;
}
TreapNode* null;
private:
// 旋转操作:d=0左转 d=1右转
void rotate(TreapNode* &o, int d) {
TreapNode* k = o->ch[d^1];o->ch[d^1] = k->ch[d];k->ch[d] = o;
// o成为k的子树 而k的另一个子树没发生变化
// 所以先维护o 在维护k
o->maintain();
k->maintain();
o = k;
}
};
struct Command{
char c;
int param1;
int param2;
};
const int maxn = 20000;
const int maxm = 60000;
const int maxc = 600000;
int n, m;
Command cmds[maxc+5];
int from[maxm + 5];
int to[maxm + 5];
bool removed[maxm + 5];
int w[maxn + 5];
//Treap
Treap treap;
TreapNode* root[maxn + 5];
// Union-find
int pa[maxn + 1];
int findset(int x) {
return pa[x] == x ? x : pa[x] = findset(pa[x]);
}
void mergeto(TreapNode* &src, TreapNode* &dst) {
if (src->ch[0] != treap.null) mergeto(src->ch[0], dst);
if (src->ch[1] != treap.null) mergeto(src->ch[1], dst);
treap.insert(dst, src->v);
delete src;
src = treap.null;
}
int main() {
freopen("input.in", "r", stdin);
char ch;
int cases = 0, x, y;
while(scanf("%d%d",&n,&m) == 2 && (n || m)) {
memset(removed, false, sizeof(removed));
REP(i, 1, n) scanf("%d", &w[i]);
REP(i, 1, m) scanf("%d%d",&from[i], &to[i]);
int cnt = 0;
for(;;) {
for(;;) {
scanf("%c",&ch);
if (isalpha(ch))
break;
}
cmds[cnt].c = ch;
if (ch == 'E') break;
if (ch == 'D') {
scanf("%d",&x);
removed[x] = true;
cmds[cnt].param1 = x;
} else if(ch == 'Q') {
scanf("%d%d",&x, &y);
cmds[cnt].param1 = x;
cmds[cnt].param2 = y;
} else {
scanf("%d%d",&x, &y);
cmds[cnt].param1 = x;
cmds[cnt].param2 = w[x];
w[x] = y;
}
++cnt;
}
REP(i, 1, n) treap.make_node(root[i], w[i]);
REP(i, 1, n) pa[i] = i;
REP(i, 1, m)
if (!removed[i]) {
x = findset(from[i]);
y = findset(to[i]);
if (x != y) {
if (root[x]->s < root[y]->s) {
mergeto(root[x], root[y]);
pa[x] = y;
} else {
mergeto(root[y], root[x]);
pa[y] = x;
}
}
}
LL sum = 0;
int query_cnt = 0;
UREP(i, cnt-1, 0) {
if (cmds[i].c == 'D') {
int e = cmds[i].param1;
int x = findset(from[e]), y = findset(to[e]);
if (x != y) {
if (root[x]->s < root[y]->s) {
mergeto(root[x], root[y]);
pa[x] = y;
} else {
mergeto(root[y], root[x]);
pa[y] = x;
}
}
} else if (cmds[i].c == 'C') {
int x = cmds[i].param1;
int v = cmds[i].param2;
int fa = findset(x);
treap.remove(root[fa], w[x]);
treap.insert(root[fa], v);
w[x] = v;
} else {
int x = findset(cmds[i].param1);
int k = cmds[i].param2;
++query_cnt;
sum += treap.kth(root[x], k);
}
}
printf("Case %d: %.6f\n",++cases, sum/(double)query_cnt);
REP(i, 1, n) treap.destroy(root[i]);
}
return 0;
}