BZOJ3862: Little Devil I

41 篇文章 0 订阅
31 篇文章 0 订阅

肯定是树剖做

分析一下3种操作
1:翻转一条链,直接翻
2:翻转与一条链相邻的边
可以发现,与一条链相邻的边中,是与父亲相连的边只有顶端的那一条,这一条可以直接暴力翻转,然后我们重新定义与链相连的边是链上每个点和儿子相邻的边中不在链上的边
考虑链上每个点和儿子的边,按轻重分类,对于轻边,我们也不需要考虑他们,因为查询时遇到的轻边是log条的,我们可以对这个点打标记,查询时暴力查询每条轻边父亲的标记
于是我们只需要考虑链上每个点与孩子的边中的重边
一次操作会遇到log条重链,每条重链中,只有截下的那段的尾端可能修改他的重儿子,其他点的重儿子都在这条链上,每条重链暴力改就行了
3:查询一条链上黑边个数
结合一下操作二和树剖查

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;

int n,m;
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int siz[maxn],son[maxn],fa[maxn],dep[maxn],w[maxn],z,top[maxn];
void dfs(const int x)
{
    siz[x]=1; son[x]=0;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x])
    {
        fa[y]=x,dep[y]=dep[x]+1;
        dfs(y);
        if(siz[son[x]]<=siz[y]) son[x]=y;
        siz[x]+=siz[y];
    }
}
void build(const int x,const int tp)
{
    w[x]=++z; top[x]=tp;
    if(son[x]) build(son[x],tp);
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa[x]&&y!=son[x])
        build(y,y);
}

struct segment
{
    int s1[maxn<<2],s2[maxn<<2];
    int flag[maxn<<2];

    void pushup(const int x){s1[x]=s1[x<<1]+s1[x<<1|1],s2[x]=s2[x<<1]+s2[x<<1|1];}
    void pushdown(const int x)
    {
        if(!flag[x]) return;
        flag[x]^=1;
        int lc=x<<1,rc=lc|1;
        swap(s1[lc],s2[lc]); flag[lc]^=1;
        swap(s1[rc],s2[rc]); flag[rc]^=1;
    }
    void build(const int x,const int l,const int r)
    {
        s1[x]=r-l+1,s2[x]=0,flag[x]=0;
        if(l==r) return;
        int mid=l+r>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    }
    int lx,rx;
    void rev(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return;
        if(lx<=l&&r<=rx) { swap(s1[x],s2[x]);flag[x]^=1;return; }
        pushdown(x);
        int mid=l+r>>1;
        rev(x<<1,l,mid); rev(x<<1|1,mid+1,r);
        pushup(x);
    }
    int que(const int x,const int l,const int r)
    {
        if(rx<l||r<lx) return 0;
        if(lx<=l&&r<=rx) return s2[x];
        int mid=l+r>>1;
        pushdown(x);
        return que(x<<1,l,mid)+que(x<<1|1,mid+1,r);
    }
    int pc[maxn];
    void clear(){for(int i=0;i<=n;i++) pc[i]=0;}
    void add(int x){for(;x<=n;x+=lowbit(x))pc[x]++;return;}
    int qp(int x){int re=0;for(;x;x-=lowbit(x))re+=pc[x];return re;}
}seg;

void reve(int x,int y)
{
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        seg.lx=w[f1],seg.rx=w[x],seg.rev(1,1,z);
        x=fa[f1],f1=top[x];
    }
    if(x==y)return;
    if(dep[x]>dep[y]) swap(x,y);
    seg.lx=w[x]+1,seg.rx=w[y],seg.rev(1,1,z);
}
int ti[maxn],nowt;
void revp(int x,int y)
{
    if(x==y)
    {
        seg.lx=seg.rx=w[x],seg.rev(1,1,z);
        if(son[x]) seg.lx=seg.rx=w[x]+1,seg.rev(1,1,z);
        seg.add(w[x]); seg.add(w[x]+1);
        return;
    }
    nowt++;
    int tx=x,ty=y;
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        seg.add(w[f1]),seg.add(w[x]+1);
        seg.lx=seg.rx=w[f1],seg.rev(1,1,z);
        x=fa[f1],f1=top[x];
        if(son[x]&&ti[w[son[x]]]!=nowt&&!(f2==f1&&dep[y]>dep[x])) 
            ti[w[son[x]]]=nowt,seg.lx=seg.rx=w[son[x]],seg.rev(1,1,z);
    }
    if(dep[x]>dep[y]) swap(x,y);
    seg.add(w[x]),seg.add(w[y]+1);
    seg.lx=seg.rx=w[x],seg.rev(1,1,z);

    if(son[tx]&&x!=tx) seg.lx=seg.rx=w[tx]+1,seg.rev(1,1,z);
    if(son[ty]&&x!=ty) seg.lx=seg.rx=w[ty]+1,seg.rev(1,1,z);
}
int query(int x,int y)
{
    int f1=top[x],f2=top[y];
    int ans=0;
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        seg.lx=w[f1],seg.rx=w[x]; ans+=seg.que(1,1,z);
        seg.lx=seg.rx=w[f1]; int tmp=seg.que(1,1,z);
        if(seg.qp(w[fa[f1]])&1) ans=ans-tmp+(!tmp);
        x=fa[f1],f1=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    if(x!=y)
    {
        seg.lx=w[x]+1,seg.rx=w[y],ans+=seg.que(1,1,z);
    }
    return ans;
}

int main()
{
    int tcase; read(tcase);
    while(tcase--)
    {
        read(n);
        len=0; for(int i=1;i<=n;i++) fir[i]=0;
        for(int i=1;i<n;i++)
        {
            int x,y; read(x); read(y);
            ins(x,y); ins(y,x);
        }
        dep[1]=1; dfs(1);
        z=0; build(1,1);
        seg.build(1,1,z); seg.clear();
        read(m);
        while(m--)
        {
            int ti,x,y; read(ti); read(x); read(y);
            if(ti==1) reve(x,y);
            if(ti==2) 
                revp(x,y);
            if(ti==3) 
                printf("%d\n",query(x,y));
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值