[uoj207][动态树]共价大爷游长沙

207. 共价大爷游长沙

统计
描述
提交
自定义测试
火车司机出秦川,跳蚤国王下江南,共价大爷游长沙。每个周末,勤劳的共价大爷都会开车游历长沙市。

长沙市的交通线路可以抽象成为一个 nn 个点 n−1n−1 条边的无向图,点编号为 11 到 nn,任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连。有一个包含一些点对 (x,y)(x,y) 的可重集合S,共价大爷的旅行路线是这样确定的:每次他会选择 SS 中的某一对点 (x,y)(x,y),并从 xx 出发沿着唯一路径到达 yy。

小L是共价大爷的脑残粉,为了见到共价大爷的尊容,小L决定守在这张图的某条边上等待共价大爷的到来。为了保证一定能见到他,显然小L必须选择共价大爷一定会经过的边——也就是所有共价大爷可能选择的路径都经过的边。

现在小L想知道,如果他守在某一条边,是否一定能见到共价大爷。

然而长沙市总是不断的施工,也就是说,可能某个时刻某条边会断开,同时这个时刻一定也有某条新边会出现,且任意时刻图都满足任意两点间均存在恰好一条路径的条件。注意断开的边有可能和加入的新边连接着相同的两个端点。共价大爷的兴趣也会不断变化,所以S也会不断加入新点对或者删除原有的点对。当然,小L也有可能在任何时候向你提出守在某一条边是否一定能见到共价大爷的问题。你能回答小L的所有问题吗?

输入格式
输入的第一行包含一个整数 idid,表示测试数据编号,如第一组数据的id=1id=1,样例数据的 idid 可以忽略。hack数据中的 idid必须为 00 到 1010 之间的整数。hack数据中idid的值和数据类型没有任何关系。

输入的第二行包含两个整数 n,mn,m,分别表示图中的点数,以及接下来会发生的事件数,事件的定义下文中会有描述。初始时 SS 为空。

接下来 n−1n−1 行,每行两个正整数 x,yx,y,表示点 xx 和点 yy 之间有一条无向边。

接下来 mm 行,每行描述一个事件,每行的第一个数 typetype 表示事件的类型。

若type=1type=1,那么接下来有四个正整数x,y,u,vx,y,u,v,表示先删除连接点xx和点yy的无向边,保证存在这样的无向边,然后加入一条连接点uu和点vv的无向边,保证操作后的图仍然满足题中所述条件。

若type=2type=2,那么接下来有两个正整数 x,yx,y,表示在 SS 中加入点对 (x,y)(x,y)。

若type=3type=3,那么接下来有一个正整数 xx,表示删除第 xx 个加入 SS 中的点对,即在第 xx 个 type=2type=2 的事件中加入 SS 中的点对,保证这个点对存在且仍然在 SS 中。

若 type=4type=4,那么接下来有两个正整数 x,yx,y,表示小L询问守在连接点 xx 和点 yy 的边上是否一定能见到共价大爷,保证存在这样的无向边且此时 SS 不为空。

输出格式
对于每个小L的询问,输出“YES”或者“NO”(均不含引号)表示小L一定能或者不一定能见到共价大爷。

样例一
input

0
5 7
1 2
1 3
2 4
1 5
2 1 5
1 1 5 2 5
4 2 5
2 1 4
4 2 5
3 1
4 2 4

output

YES
NO
YES

explanation
最开始将点对 (1,5)(1,5) 加入到 SS 中,此时点 11 和点 55 之间的路径是 1→51→5。

接着将连接点 11 和点 55 的边断开,加入连接点 22 和点 55 的边,我们发现图仍然满足题中所述条件,且点 11 和点 55 之间的路径是 1→2→51→2→5,经过点了 22 和点 55 之间的边,因此第一个询问答案是 YES。

接着将点对 (1,4)(1,4) 加入到 SS 中,点 11 和点 44 之间的路径是 1→2→41→2→4,没有经过点 22 和点 55 之间的边,因此第二个询问答案是 NO。

接着,我们删除了第一个加入到 SS 中的点对,也就是点对 (1,5)(1,5),此时 SS 中唯一的点对就是 (1,4)(1,4),经过了点 22 和点 44 之间的边,因此最后一个询问答案是 YES。

样例二
见样例数据下载。

样例三
见样例数据下载。这组数据中 type≠1type≠1。

限制与约定
每组测试数据的限制与约定如下所示:

测试点编号 nn mm type=type= 限制与约定
1 n≤100n≤100 m≤100m≤100 1,2,3,41,2,3,4
2 n≤100000n≤100000 m≤300000m≤300000 2,42,4
3
4 2,3,42,3,4
5
6 1,2,3,41,2,3,4 任意时刻 |S|≤10|S|≤10
7
8
9
10
时间限制:2s2s
空间限制:512MB512MB
来源
matthew99

题解
http://matthew99.blog.uoj.ac/blog/1771

下载
样例数据下载

sol:

对于每一组点对给他们赋一个随机值,那么询问所有点对是否经过x,y这条边实际上就是看这些点对是否全部都是分布在两侧的。所以我们维护每个点的子树的xor值。只要在x为根的树里面检查y的子树的xor值为全部点对的xor值的话就说明这很大概率是一组可行解(如果你的随机值足够大,数据足够大)。

ps:1A还是非常舒服的,就是第一遍NO输出成了No

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;

inline int read()
{
    char c;
    bool pd=0;
    while((c=getchar())>'9'||c<'0')
    if(c=='-') pd=1;
    int res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=(res<<3)+(res<<1)+c-'0';
    return pd?-res:res;
}
const int N=310000;
int n,m,tmp,ans;
int sum1[N],sum2[N];
int val[N],lc[N],rc[N],fa[N];
int key[N],tkey;
inline void updata(int x)
{
    sum1[x]=sum2[x]^sum1[lc[x]]^sum1[rc[x]]^val[x];
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=lc[y]==x?rc[x]:lc[x];
    if(b) fa[b]=y;
    fa[x]=z;fa[y]=x;
    if(z)
    {
        if(lc[z]==y) lc[z]=x;
        if(rc[z]==y) rc[z]=x;
    }
    if(lc[y]==x) rc[x]=y,lc[y]=b;
    else lc[x]=y,rc[y]=b;
    updata(y);
}
int rev[N];
inline void tag_rev(int x)
{
    rev[x]=!rev[x];
    swap(lc[x],rc[x]);
}
inline void tag_down(int x)
{
    if(rev[x])
    {
        tag_rev(lc[x]);
        tag_rev(rc[x]);
        rev[x]=0;
    }
}
int sta[N];
inline bool is_root(int x)
{
    return lc[fa[x]]!=x&&rc[fa[x]]!=x;
}
inline void splay(int x)
{
    sta[sta[0]=1]=x;
    for(int y=x;!is_root(y);y=fa[y]) sta[++sta[0]]=fa[y];
    while(sta[0]) tag_down(sta[sta[0]--]);
    while(!is_root(x))
    {
        if(!is_root(fa[x]))
        {
            if((lc[fa[fa[x]]]==fa[x])==(lc[fa[x]]==x)) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
    updata(x);
}
inline void access(int q)
{
    for(int p=0;q;p=q,q=fa[q])
    {
        splay(q);
        sum2[q]^=sum1[rc[q]];
        rc[q]=p;
        sum2[q]^=sum1[rc[q]];
        updata(q);
    }
}
inline void make_root(int x)
{
    access(x);
    splay(x);
    tag_rev(x);
}
inline void link(int x,int y)
{
    make_root(x);
    access(y);
    splay(y); 
    fa[x]=y;
    sum2[y]^=sum1[x];
    updata(y);
}
inline void cut(int x,int y)
{
    make_root(x);
    access(y);
    splay(x);
    rc[x]=fa[y]=0;
    updata(x);
}
struct cc
{
    int x,y,z;
}a[N];
int an;
int main()
{
//  freopen("207.in","r",stdin);
    n=read();
    n=read();
    m=read();
    int x,y;
    srand(time(0));
    for(int i=2;i<=n;++i)
    {
        x=read();
        y=read();
        link(x,y);
    }
    int tp,u,v;
    for(int i=1;i<=m;++i)
    {
        tp=read();
        if(tp==1)
        {
            x=read();y=read();
            u=read();v=read();
            cut(x,y);
            link(u,v);
        }   
        if(tp==2)
        {
            ++an;
            a[an].z=rand()<<15|rand();
            a[an].x=x=read();
            a[an].y=y=read();
            make_root(x);
            sum1[x]^=a[an].z;
            val[x]^=a[an].z;
            make_root(y);
            sum1[y]^=a[an].z;
            val[y]^=a[an].z;
            tkey^=a[an].z;
        }
        if(tp==3)
        {
            v=read();
            x=a[v].x;
            y=a[v].y;
            u=a[v].z;
            make_root(x);
            val[x]^=u;
            sum1[x]^=u;
            make_root(y);
            val[y]^=u;
            sum1[y]^=u;
            tkey^=u;
        }
        if(tp==4)
        {
            x=read();
            y=read();
            make_root(x);
            access(y);
            splay(x);
            if(tkey==sum1[y]) printf("YES\n");
            else printf("NO\n");
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值