训练赛第二场补题&&总结(更新中)

B题:

比赛的时候我又没仔细看题,又把题目读错了。。。虽然读对的话我也不一定能想出如何用加权并查集做。比赛的时候我的第一感觉就是splay模拟,赛后写了一下,TLE了。。。

#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再改边权,因为是往上寻找,所以从祖先开始往下更新,说的比较含糊,其实是很简单很容易理解的东西。

#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

 

posted @ 2016-03-12 10:35  __560  阅读(294)  评论(0编辑  收藏  举报