訓練賽第二場補題&&總結(更新中)

B題:node

比賽的時候我又沒仔細看題,又把題目讀錯了。。。雖然讀對的話我也不必定能想出如何用加權並查集作。比賽的時候個人第一感受就是splay模擬,賽後寫了一下,TLE了。。。ios

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))
#define key_val ch[ch[rt][1]][0]

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=1e9+10;

int n,q;
char op[10];int x,y;
int node[maxn];/// node[x] x所在結點
///--- splay
int pre[maxn],ch[maxn][2];
int val[maxn];
int sz[maxn];
int tot1,s[maxn],tot2;

void newnode(int &x,int fa,int k)
{
    if(tot2) x=s[tot2--];
    else x=++tot1;
    pre[x]=fa;
    val[x]=k;
    sz[x]=1;
    MS0(ch[x]);
}

void up(int x)
{
    sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
}

void rot(int x,int kind)
{
    int y=pre[x];
    ch[y][kind^1]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    up(y);
}

void splay(int x,int goal)
{
    while(pre[x]!=goal){
        if(pre[pre[x]]==goal) rot(x,ch[pre[x]][0]==x);
        else{
            int y=pre[x],z=pre[y];
            int kind=ch[y][0]==x,one=0;
            if(ch[y][0]==x&&ch[z][0]==y) one=1;
            if(ch[y][1]==x&&ch[z][1]==y) one=1;
            if(one) rot(y,kind),rot(x,kind);
            else rot(x,kind),rot(x,kind^1);
        }
    }
    up(x);
}

void Init_splay()
{
    pre[0]=0;
    MS0(ch[0]);
    sz[0]=0;
    tot1=tot2=0;
}

void Init()
{
    Init_splay();
    int rt;
    REP(i,1,n) newnode(rt,0,i),node[i]=rt;
}

///----solve
int Calc(int x)
{
    int y=node[x];
    splay(y,0);
    return sz[ch[y][0]];
}

void Put(int x,int y)
{
    int z=node[x],w=node[y];
    if(z==w) return;
    splay(z,0);
    while(ch[z][0]) z=ch[z][0];
    splay(z,0);
    splay(w,0);
    ch[z][0]=w;
    pre[w]=z;
    splay(w,0);
}

int main()
{
    freopen("in.txt","r",stdin);
    while(~scanf("%d",&q)){
        n=30010;
        Init();
        REP(i,1,q){
            scanf("%s",op);
            if(op[0]=='M'){
                scanf("%d%d",&x,&y);
                Put(x,y);
            }
            else{
                scanf("%d",&x);
                printf("%d\n",Calc(x));
            }
        }
    }
    return 0;
}
View Code

沒辦法,學並查集的思路吧,做爲專搞數據結構的,這種加權並查集的題目沒作出來實在慚愧。。數據結構

 好了,我已經知道會這道題了,並非我思惟不行,實在是這是我作的第一道加權並查集的題目。加權並查集就是將x和fa[x]這條樹邊的邊權存在x的點權中,查找路徑壓縮的時候先find再改邊權,由於是往上尋找,因此從祖先開始往下更新,說的比較含糊,實際上是很簡單很容易理解的東西。ide

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define REP(i,a,b) for(int i=a;i<=b;i++)
#define MS0(a) memset(a,0,sizeof(a))

using namespace std;

typedef long long ll;
const int maxn=1000100;
const int INF=1e9+10;

int n,q;
int fa[maxn],sz[maxn],w[maxn];
char op[10];int x,y;

int find(int x)
{
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    w[x]+=w[fa[x]];
    return fa[x]=t;
}

void Union(int u,int v)
{
    int x=find(u),y=find(v);
    if(x==y) return;
    fa[x]=y;
    w[x]=sz[y];
    sz[y]+=sz[x];
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    while(~scanf("%d",&q)){
        n=30010;
        REP(i,0,n) fa[i]=i,w[i]=0,sz[i]=1;
        while(q--){
            scanf("%s",op);
            if(op[0]=='M') scanf("%d%d",&x,&y),Union(x,y);
            else scanf("%d",&x),find(x),printf("%d\n",w[x]);
        }
    }
    return 0;
}
/**

10
M 1 6
M 2 4
M 2 6
M 6 5
C 1
C 2
C 3
C 4
C 5
C 6
5
M 1 6
M 2 4
M 2 6
M 6 5
C 2
*/
View Code
相關文章
相關標籤/搜索