1. 程式人生 > >HNOI2012 永無鄉

HNOI2012 永無鄉

struct spa noi2012 ger swap 行合並 %s pri names

傳送門

這道題一開始看……能想出來用splay和並查集維護,不過,怎麽把兩棵splay合並呢……?暴力拆開一個一個合並?

後來發現真的是這樣……不過其實是啟發式合並,也就是每次我們合並兩棵splay的時候,總是把小的那棵合並到大的那棵上面。這樣的話就能保證每個點最多之被合並logn次(別問我為啥,我也不知道)

具體的操作就是,我們在小的那棵splay上進行dfs,如果這個節點有左/右兒子就向下走,然後無路可走的時候就把它insert到大的那棵splay裏面。其他的一切操作都很熟悉。然後每次用並查集維護,註意這次的splay操作都是要從每個節點自己對應的root開始的。

不知道為什麽,我的代碼會MLE/RE一個點。所以最後還是抄了yyb大神的代碼才過的……

看一下代碼吧(90pts)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define
enter putchar(‘\n‘) #define de putchar(‘#‘) #define pr pair<int,int> #define mp make_pair #define fi first #define sc second #define pb push_back using namespace std; typedef long long ll; const int M = 4000005; const int N = 10000005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar();
while(ch < 0 || ch > 9) { if(ch == -) op = -1; ch = getchar(); } while(ch >=0 && ch <= 9) { ans *= 10; ans += ch - 0; ch = getchar(); } return ans * op; } struct node { int ch[2],fa,son,val; }t[M]; int n,m,f[M],root[M],tot,idx,num[M],x,y,q; char s[10]; int getfa(int x) { return (x == f[x]) ? x : f[x] = getfa(f[x]); } bool get(int x) { return t[t[x].fa].ch[1] == x; } void pushup(int x) { t[x].son = t[t[x].ch[0]].son + t[t[x].ch[1]].son + 1; } void rotate(int x) { int y = t[x].fa,z = t[y].fa,k = get(x); t[z].ch[get(y)] = x,t[x].fa = z; t[y].ch[k] = t[x].ch[k^1],t[t[y].ch[k]].fa = y; t[x].ch[k^1] = y,t[y].fa = x; pushup(x),pushup(y); } 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[0] == x) ^ (t[z].ch[0] == y) ? rotate(x) :rotate(y); rotate(x); } if(goal <= n) root[goal] = x; } void insert(int x,int b) { int u = root[b],f = b; while(u && x != t[u].val) f = u,u = t[u].ch[x > t[u].val]; u = ++tot; if(f > n) t[f].ch[x > t[f].val] = u; t[u].son = 1; t[u].ch[0] = t[u].ch[1] = 0; t[u].fa = f,t[u].val = x; splay(u,b); } void dfs(int x,int g) { if(t[x].ch[0]) dfs(t[x].ch[0],g); if(t[x].ch[1]) dfs(t[x].ch[1],g); insert(t[x].val,g); } void merge(int x,int y) { int r1 = getfa(x),r2 = getfa(y); if(r1 == r2) return; if(t[root[r1]].son > t[root[r2]].son) swap(r1,r2); f[r1] = r2; dfs(root[r1],r2); } int rk(int x,int k) { int u = root[x]; if(t[u].son < k) return -1; while(1) { int y = t[u].ch[0]; if(t[y].son + 1 < k) k -= (t[y].son + 1),u = t[u].ch[1]; else if(t[y].son >= k) u = y; else return t[u].val; } } int main() { n = read(),m = read(); rep(i,1,n) { root[i] = i+n,f[i] = i; x = read(),num[x] = i; t[i+n].val = x,t[i+n].son = 1,t[i+n].fa = i; } tot = n << 1; rep(i,1,m) x = read(),y = read(),merge(x,y); q = read(); while(q--) { scanf("%s",s); x = read(),y = read(); if(s[0] == B) merge(x,y); else { int g = rk(getfa(x),y); (g == -1) ? printf("-1\n") : printf("%d\n",num[g]); } } return 0; }

yyb大神的AC代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
#define MAX 500000
inline int read()
{
    register int x=0,t=1;
    register char ch=getchar();
    while(ch!=-&&(ch<0||ch>9))ch=getchar();
    if(ch==-){t=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-48;ch=getchar();}
    return x*t;
}
struct Node
{
    int ch[2];
    int val,ff,size;
}t[MAX];
int f[MAX];
int root[MAX],tot;
int hh[MAX];
int N,M;
int getf(int x)
{
    return x==f[x]?x:f[x]=getf(f[x]);
}
inline void pushup(int x)
{
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}
//1..N分別為N棵splay的0節點
//每次都對splay進行合並
inline void rotate(int x)
{
    int y=t[x].ff;
    int z=t[y].ff;
    int k=t[y].ch[1]==x;
    t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
    t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;t[y].ff=x;
    pushup(y);pushup(x);
}
inline void splay(int x,int goal)
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff,z=t[y].ff;
        if(z!=goal)
            (t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);
        rotate(x);
    }
    if(goal<=N)root[goal]=x;//如果是某一個0節點的下方,則更新當前splay的根節點
}
inline void insert(int x,int bh)
{
    int u=root[bh],ff=bh;
    while(u&&t[u].val!=x)
        ff=u,u=t[u].ch[x>t[u].val];
    u=++tot;
    t[u].size=1;
    t[u].ff=ff;
    if(ff>N)
        t[ff].ch[x>t[ff].val]=u;
    t[u].val=x;t[u].ch[0]=t[u].ch[1]=0;
    splay(u,bh);
}
void DFS(int u,int kk)//遍歷整顆splay
{
    if(t[u].ch[0])DFS(t[u].ch[0],kk);
    if(t[u].ch[1])DFS(t[u].ch[1],kk);
    insert(t[u].val,kk);//合並到另外一顆splay中
}
inline void Merge(int a,int b)
{
    int x=getf(a),y=getf(b);
    if(x==y)return;//已經在一個集合內
    if(t[root[x]].size>t[root[y]].size)swap(x,y);//強制將小的合並到大的
    f[x]=y;
    DFS(root[x],y);
}
int kth(int bh,int k)
{
    int u=root[bh];
    if(t[u].size<k)return -1;
    while(233)
    {
        if(t[t[u].ch[0]].size+1<k)//在右子樹中找
        {
            k-=t[t[u].ch[0]].size+1;
            u=t[u].ch[1];
        }
        else
            if(t[t[u].ch[0]].size>=k)//在左子樹中找
                u=t[u].ch[0];
            else
                return t[u].val;//當前節點
    }
}
int main()
{
    N=read();M=read();
    for(int i=1;i<=N;++i)root[i]=i+N,f[i]=i;
    tot=N+N;
    for(int i=1;i<=N;++i)
    {
        int x=read();
        hh[x]=i;
        t[i+N].val=x;t[i+N].size=1;t[i+N].ff=i;
    }
    for(int i=1;i<=M;++i)
    {
        int x=read(),y=read();
        Merge(x,y);
    }
    int Q=read();
    while(Q--)
    {
        char ch[3];int a,b;
        scanf("%s",ch);a=read(),b=read();
        if(ch[0]==B)
        {
            Merge(a,b);
        }
        else
        {
            int ans=kth(getf(a),b);
            printf("%d\n",ans==-1?ans:hh[ans]);
        }
    }
    return 0;
}

HNOI2012 永無鄉