UvaLive 5031 Graph and Queries(Treap+并查集)

题目地址: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<n2,则将T1合并到T2,否则将T2合并到T1(即启发式合并,合并对应将一棵树所有点插入到另一棵树中)。查询操作对应名次树中求第k大值。修改时,记录每个点上一次修改时的值,则每次修改对应从树中删除当前值,然后插入该点的上一个值。

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#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<int, int> 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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值