题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3032
思路:
1.三种操作:删除编号x的边;计算与结点x连通的点中,第k大权值;修改结点x的权值为c。
2.离线操作,按命令相反的顺序进行(先删除所有要删除的边,修改所有权值)。则操作变为加边和修改点的权值。
3.对于每个点建立一棵名次树。加边可通过并查集维护,对于边u-v,若点u与点v已连通则忽略,否则将点u与点v对应树合并:设u对应T1,点数为n1;v对应T2,点数为n2,若n1
#include
#include
#include
#include
#include
#include
#define debug
using namespace std;
struct Node
{
Node* ch[2];
int r, v, s;
Node(int v) :v(v) { ch[0] = ch[1] = NULL; r = rand(); s = 1; }
bool operator < (const Node& rhs) const
{
return r < rhs.r;
}
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;
}
};
struct Treap
{
void rotate(Node* &o, int d)
{
Node* k = o->ch[d ^ 1];
o->ch[d ^ 1] = k->ch[d]; k->ch[d] = o;
o->maintain(); k->maintain(); o = k;
}
void insert(Node* &o, int x)
{
if (o == NULL)
{
o = new Node(x);
}
else
{
int d = (x < o->v ? 0 : 1);
insert(o->ch[d], x);
if (o->ch[d] > 0) rotate(o, d ^ 1);
}
o->maintain();
}
void remove(Node* &o, int x)
{
int d = o->cmp(x);
if (d == -1)
{
Node* u = o;
if (o->ch[0] != NULL&&o->ch[1] != NULL)
{
int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
rotate(o, d2); remove(o->ch[d2], x);
}
else
{
if (o->ch[0] == NULL) o = o->ch[1];
else o = o->ch[0];
delete u;
}
}
else
{
remove(o->ch[d], x);
}
if (o != NULL) o->maintain();
}
int kth(Node* 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 mergeto(Node* &src, Node* &dest)
{
if (src->ch[0] != NULL) mergeto(src->ch[0], dest);
if (src->ch[1] != NULL) mergeto(src->ch[1], dest);
insert(dest, src->v);
delete src;
src = NULL;
}
void removetree(Node* &x)
{
if (x->ch[0] != NULL) removetree(x->ch[0]);
if (x->ch[1] != NULL) removetree(x->ch[1]);
delete x;
x = NULL;
}
};
const int maxn = 20000 + 50;
const int maxm = 60000 + 50;
struct Que
{
int a, b, id;
Que(){}
Que(int a,int b,int c):a(a),b(b),id(c){}
};
int n, m;
Treap tree;
int fa[maxn];
Node* root[maxn];
Que q[maxn * 30];
pair e[maxm];
int w[maxn], flag[maxm];
void init()
{
memset(w, 0, sizeof(w));
memset(flag, 0, sizeof(flag));
for (int i = 1; i <= n; i++) fa[i] = i;
}
int Find(int x)
{
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void addEdge(int x, int y)
{
int u = Find(x),v = Find(y);
if (u == v) return;
if (root[u]->s < root[v]->s)
{
tree.mergeto(root[u], root[v]);
fa[u] = v;
}
else
{
tree.mergeto(root[v], root[u]);
fa[v] = u;
}
}
int main()
{
#ifdef debu
freopen("in.txt", "r", stdin);
#endif // debug
int cas = 0;
srand((int)time(NULL));
while (scanf("%d%d", &n, &m) != EOF && (n + m))
{
init();
for (int i = 1; i <= n; i++)
{
scanf("%d", &w[i]);
}
for (int i = 1; i <= m; i++) scanf("%d%d", &e[i].first, &e[i].second);
char ch;
int cnt = 0;
while (scanf("%c", &ch))
{
if (ch == 'E') break;
if (ch == 'Q')
{
int u, k;
scanf("%d%d", &u, &k);
q[++cnt] = Que(u, k, 2);
}
else if (ch == 'C')
{
int u, c;
scanf("%d%d", &u, &c);
int tmp = w[u];
w[u] = c;
q[++cnt] = Que(u, tmp, 0);
}
else if (ch == 'D')
{
int e;
scanf("%d", &e);
flag[e] = 1;
q[++cnt] = Que(e, 0, 1);
}
}
for (int i = 1; i <= n; i++)
{
if (root[i] != NULL) tree.removetree(root[i]);
root[i] = new Node(w[i]);
}
for (int i = 1; i <= m; i++)
{
if (!flag[i])
{
addEdge(e[i].first, e[i].second);
}
}
int sum = 0;
double ans = 0.0;
for (int i = cnt; i >=1; i--)
{
if (q[i].id == 2)
{
ans += tree.kth(root[Find(q[i].a)], q[i].b);
sum++;
}
else if (q[i].id == 1)
{
addEdge(e[q[i].a].first, e[q[i].a].second);
}
else if (q[i].id == 0)
{
int rt = Find(q[i].a);
tree.remove(root[rt], w[q[i].a]);
tree.insert(root[rt], q[i].b);
w[q[i].a] = q[i].b;
}
}
printf("Case %d: %.6f\n", ++cas,ans/sum);
}
return 0;
}