洛谷SP16549 QTREE6 - Query on a tree VI题解

题目传送门

题目描述

给你一棵n个点的树,编号1~n。每个点可以是黑色,可以是白色。初始时所有点都是黑色。下面有两种操作请你操作给我们看:

0 u:询问有多少个节点v满足路径u到v上所有节点(包括)都拥有相同的颜色
1 u:翻转u的颜色

AC代码

#include<cstdio>
#include<cstdlib>
#define R register int
#define I inline void
const int N=1000009,M=N<<1;
#define lc c[x][0]
#define rc c[x][1]
#define C col[u]
int fa[N],he[N],ne[M],to[M];
bool col[N];
struct LCT{
    int f[N],c[N][2],si[N],s[N],h[N];
    bool r[N];
    LCT(){for(R i=1;i<N;++i)s[i]=1;}//注意初始化
    inline bool nroot(R x){return c[f[x]][0]==x||c[f[x]][1]==x;}
    I pushup(R x){
        s[x]=s[lc]+s[rc]+si[x]+1;
    }
    I rotate(R x){
        R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
        if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;
        f[w]=y;f[y]=x;f[x]=z;
        pushup(y);
    }
    I splay(R x){
        R y;
        while(nroot(x)){
            if(nroot(y=f[x]))rotate((c[f[y]][0]==y)^(c[y][0]==x)?x:y);
            rotate(x);
        }
        pushup(x);
    }
    I access(R x){
        for(R y=0;x;x=f[y=x]){
            splay(x);
            si[x]+=s[rc];
            si[x]-=s[rc=y];
        }
    }
    inline int findroot(R x){
        access(x);splay(x);
        while(lc)x=lc;
        splay(x);
        return x;
    }
    I link(R x){//只传一个参数,因为只会连父边,cut同理
        splay(x);//不用access(x),因为x一定是连通块的根
        R y=f[x]=fa[x];
        access(y);splay(y);//与常规LCT不同,别忘加
        si[y]+=s[x];s[y]+=s[x];
    }
    I cut(R x){
        access(x);splay(x);
        lc=f[lc]=0;
        pushup(x);
    }
}lct[2];
void dfs(R x){
    for(R y,i=he[x];i;i=ne[i])
        if((y=to[i])!=fa[x])
            fa[y]=x,dfs(y),lct[0].link(y);
}
#define G ch=getchar()
#define in(z) G;\
    while(ch<'-')G;\
    z=ch&15;G;\
    while(ch>'-')z*=10,z+=ch&15,G
int main(){
    register char ch;
    R p=1,n,m,i,u,v,op;
    in(n);
    for(i=1;i<n;++i){
        in(u);in(v);
        to[++p]=v;ne[p]=he[u];he[u]=p;
        to[++p]=u;ne[p]=he[v];he[v]=p;
    }
    dfs(1);
    fa[1]=n+1;lct[0].link(1);//虚点
    in(m);
    while(m--){
        in(op);in(u);
        if(op)lct[C].cut(u),lct[C^=1].link(u);
        else{
            v=lct[C].findroot(u);
            printf("%d\n",lct[C].s[lct[C].c[v][1]]);
        }
    }
    return 0;
}

SPOJ资料

SPOJ是波兰最为出色的Online Judge之一,界面和谐,题目类型也非常丰富,适合有一定基础的选手练习,对高手而言也是个提高能力的良好平台。

SPOJ题目分类:classical,challenge,partial,tutorial。

1)classical:ACM题型,通过所有数据才能算AC

2)challenge:有趣的题目,每个题目有不同的评分标准(代码长短,效果好坏,速度等),感觉都挺难得,至今没敢碰。

3)partial:OI题型,根据通过的测试数据比例,得到部分分。

4)tutorial:ACM题型,题目算法都比较基础(也有几道bt题放在里面)。

SPOJ得分:classical得分,challenge得分,partial和tutorial是供用户练习或训练使用,不计入SPOJ得分中。

1)classical得分:得到80/(40+这道题目通过人数),也就是说过的人越多得分越低,过的人越少得分越高,根据这个公式,每个用户的分数都是变动的。

扫描二维码关注公众号,回复: 5398272 查看本文章

2)challenge得分:按该题评分标准计算,最优者得到3分(很诱人,不过好难啊T_T),其他用户,得到一个与最优者的相对得分(<1分)。

SPOJ吸引人的地方在于:
1)它所提供的编程语言达30种,甚至有些题目要求使用最简单的语言Brainfuck去解决,虽然编程过程非常痛苦,但是AC这类题的喜悦也是其他题目所不能比较的。
2)跟大部分的OJ不同(SGU、Ural用的全都是自己的题目,POJ、HDU、ZOJ、TOJ则主要是历年Regional和大小型比赛的题目),SPOJ题目都是由用户(或管理员)推荐的,OJ中有不少的own problem,除此以外也挑选出各种比赛的中档以上的题目,删去了最简单的题目,有时也会把一些绝对”大自然“的题目删去了,特别值得一提的是,当一些低复杂度的算法被发现后,某些题目会相继挂出他们的加强版,这些题目往往能提高大家的个人能力。

目前,虽然SPOJ的访问量不能媲美当年的ZOJ和现在POJ、HDU,在国内做的人也不算特别多,但它的题目质量确实非常不错(T_T,可惜我主要在切水题),且题库一直都在更新,我相信它会越来越受欢迎。国内(甚至国外)我都没找到关于SPOJ比较全面的介绍和关于题目的解题报告,为了方便大家,在遇到困难的时候能得到一些帮助,我希望尽自己微薄的力量,争取写一些简略的解题报告,填充SPOJ Solution这片空白。

猜你喜欢

转载自blog.csdn.net/yingyouyu/article/details/87895555