• [题解] [HNOI2012] 永无乡


    题面

    题解

    题面很清楚

    问题是要怎么做

    其实就是查询一个动态集合的第 (k)

    每次合并就把两个集合黏在一起就行了

    我们可以想到用 splay 来写, 启发式合并一下就行

    还有一种思路是权值线段树合并

    每一次连边就相当于是一次合并

    好像确实没有什么很难想的地方, 思路很顺啊

    就是线段树合并的复杂度实在是不会分析

    这里给出两种方式的代码

    Code

    splay

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 100005; 
    using namespace std;
    
    int n, m, q, pa[N], rt[N]; 
    struct Tree { int ch[2], fa, sz, val; } t[N]; 
    char s[15]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    int getf(int x) { return pa[x] ^ x ? pa[x] = getf(pa[x]) : x; }
    
    void pushup(int p) { t[p].sz = t[t[p].ch[0]].sz + t[t[p].ch[1]].sz + 1; }
    
    void rotate(int x)
    {
    	int y = t[x].fa, z = t[y].fa, k = t[y].ch[1] == x;
    	t[z].ch[t[z].ch[1] == y] = x, t[x].fa = z;
    	t[y].ch[k] = t[x].ch[k ^ 1], t[t[x].ch[k ^ 1]].fa = y;
    	t[x].ch[k ^ 1] = y, t[y].fa = x;
    	pushup(y), pushup(x); 
    }
    
    void splay(int x, int goal)
    {
    	while(t[x].fa != goal)
    	{
    		int y = t[x].fa, z = t[y].fa;
    		if(z != goal)
    			(t[y].ch[1] == x) ^ (t[z].ch[1] == y) ? rotate(x) : rotate(y);
    		rotate(x); 
    	}
    }
    
    void insert(int x, int u)
    {
    	splay(u, 0); int ff = 0; 
    	while(u)
    		ff = u, u = t[u].ch[t[x].val > t[u].val];
    	t[x].ch[0] = t[x].ch[1] = 0, t[x].sz = 1, t[x].fa = ff;
    	if(ff) t[ff].ch[t[x].val > t[ff].val] = x;
    	splay(x, 0); 
    }
    
    void modify(int u, int fa)
    {
    	if(!u) return;
    	modify(t[u].ch[0], fa); 
    	modify(t[u].ch[1], fa); 
    	insert(u, fa); 
    }
    
    void merge(int u, int v)
    {
    	u = getf(u), v = getf(v); 
    	splay(u, 0), splay(v, 0); 
    	if(t[u].sz < t[v].sz) swap(u, v); 
    	modify(v, u); 
    }
    
    void output(int u)
    {
    	if(!u) return;
    	output(t[u].ch[0]);
    	printf("%d ", u);
    	output(t[u].ch[1]); 
    }
    
    int kth(int x, int y)
    {
    	int u = getf(x); splay(u, 0); 
    	if(t[u].sz < y) return -1; 
    	while(u)
    	{
    		if(y <= t[t[u].ch[0]].sz)
    			u = t[u].ch[0];
    		else if(y > t[t[u].ch[0]].sz + 1)
    			y -= t[t[u].ch[0]].sz + 1, u = t[u].ch[1];
    		else return u; 
    	}
    }
    
    int main()
    {
    	n = read <int> (), m = read <int> ();
    	for(int i = 1; i <= n; i++)
    		t[i].val = read <int> (), t[i].sz = 1, pa[i] = i; 
    	for(int u, v, i = 1; i <= m; i++)
    	{
    		u = getf(u = read <int> ()), v = getf(v = read <int> ());
     		if(u == v) continue; 
    		merge(u, v), pa[getf(v)] = getf(u); 
    	}
    	q = read <int> (); 
    	int x, y; 
    	while(q--)
    	{
    		scanf("%s", s + 1), x = read <int> (), y = read <int> ();
    		if(s[1] == 'B')
    		{
    			x = getf(x), y = getf(y);
    			if(x == y) continue;
    			merge(x, y), pa[getf(y)] = getf(x); 
    		}
    		if(s[1] == 'Q')
    			printf("%d
    ", kth(x, y)); 
    	}
    	return 0; 
    }
    /*
      7 3
    1 3 6 2 5 7 4
    1 4
    4 7
    4 6
    4
    B 2 3
    B 2 5
    B 5 4
    Q 4 2
     */
    
    

    我没写 else 调了一个多小时(滑稽

    线段树合并

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 100005; 
    using namespace std;
    
    int n, m, q, pa[N], rt[N], cnt; 
    struct Tree { int l, r, sz, id; } t[N * 32]; 
    char s[15]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    int getf(int x) { return pa[x] ^ x ? pa[x] = getf(pa[x]) : x; }
    
    void modify(int &p, int l, int r, int k, int x)
    {
    	p = ++cnt; t[p].sz++;
    	if(l == r)
    		return (void) (t[p].id = x); 
    	int mid = (l + r) >> 1; 
    	if(k <= mid) modify(t[p].l, l, mid, k, x);
    	else modify(t[p].r, mid + 1, r, k, x); 
    }
    
    int merge(int p, int o, int l, int r)
    {
    	if(!p || !o) return p + o; 
    	t[p].sz += t[o].sz;
    	if(l == r)
    		return t[p].id = t[p].id + t[o].id, p; 
    	int mid = (l + r) >> 1;
    	t[p].l = merge(t[p].l, t[o].l, l, mid);
    	t[p].r = merge(t[p].r, t[o].r, mid + 1, r);
    	return p; 
    }
    
    int query(int p, int l, int r, int k)
    {
    	if(l == r)
    		return t[p].id;
    	int mid = (l + r) >> 1, sum = t[t[p].l].sz;
    	if(k <= sum) return query(t[p].l, l, mid, k);
    	else return query(t[p].r, mid + 1, r, k - sum); 
    }
    
    int main()
    {
    	n = read <int> (), m = read <int> ();
    	for(int x, i = 1; i <= n; i++)
    		pa[i] = i, x = read <int> (), modify(rt[i], 1, n, x, i);
    	for(int u, v, i = 1; i <= m; i++)
    	{
    		u = getf(u = read <int> ()), v = getf(v = read <int> ());
    		if(u == v) continue;
    		rt[u] = merge(rt[u], rt[v], 1, n), pa[getf(v)] = getf(u); 
    	}
    	q = read <int> (); 
    	int x, y; 
    	while(q--)
    	{
    		scanf("%s", s + 1), x = read <int> (), y = read <int> (); 
    		if(s[1] == 'B')
    		{
    			x = getf(x), y = getf(y);
    			if(x == y) continue;
    			merge(rt[x], rt[y], 1, n), pa[getf(y)] = getf(x); 
    		}
    		if(s[1] == 'Q')
    		{
    			printf("%d
    ", t[rt[getf(x)]].sz < y ? -1 : query(rt[getf(x)], 1, n, y));
    		}
    	}
    	return 0; 
    }
    
    

    超级好写, 一下就写完了

  • 相关阅读:
    osx 编译安装配置 ruby on rails
    tls/ssl证书生成和格式转换
    nginx相关的一些记录
    用systemd脚本自动启动node js程序
    SSH Tunneling
    c代码读取目录信息
    用Qt Creator 对 leveldb 进行简单的读写
    centos 7 相关的一些记录
    发现一段精简的模板算法(非原创)
    几个常用的散列算法
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12189674.html
Copyright © 2020-2023  润新知