bzoj2243 树链剖分

Description

神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。

有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。

神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?

Input

输入第一行为两个整数n和m,分别代表树上苹果的个数和前来膜拜的人数。

接下来的一行包含n个数,第i个数代表编号为i的苹果的颜色Coli。

接下来有n行,每行包含两个数x和y,代表有一根树枝连接了苹果x和y(或者根和一个苹果)。

接下来有m行,每行包含四个整数u、v、a和b,代表这个人要数苹果u到苹果v的颜色种数,同时这个人认为颜色a就是颜色b。如果a=b=0,则代表这个人没有患色盲症。

Output

输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。

Sample Input

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

Sample Output

2
1
2

HINT

0<=x,y,a,b<=N

N<=50000

1<=U,V,Coli<=N

距离上一次做这道题已经过了1个月了,不得不感慨当年还是太年轻。
这道题可以算作树链剖分的裸题,注意一下端点颜色相同的线段的合并就行了。

*第一次做题时一直没有想通query时swap(u,v)之后u所在的链和v所在的链的链顶颜色(cet[1]和cet[2])怎样保持正确性。其实很简单,在swap(u,v)后swap(cet[1],cet[2])就可以啦。
*swap的条件 if(dep[top[u]] < dep[top[v]] )

/**************************************************************
    Problem: 2243
    User: DD_D
    Language: C++
    Result: Accepted
    Time:5168 ms
    Memory:14548 kb
****************************************************************/


#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m;
int col[N];
int to[2*N],nxt[2*N],head[N],etot;
int dep[N],son[N],size[N],fat[N];
int in[N],out[N],seq[N],idc,top[N];
int cet[5];
struct node{
    node *ls,*rs;
    int cof,cor,fg,num;
    void pushdown(){
        if(fg!=-1){
            //把它底下的要更新了
            ls->fg=rs->fg=fg;
            ls->cof=ls->cor=rs->cof=rs->cor=fg;
            ls->num=1;rs->num=1;
            fg=-1;
        }
    }
    void update(){
        cof=ls->cof;
        cor=rs->cor;
        if(ls->cor==rs->cof) num=ls->num+rs->num-1;
        else num=ls->num+rs->num;
    }
}pool[2*N+5],*tail=pool,*rt;
void adde(int u,int v)
{
    to[++etot]=v;
    nxt[etot]=head[u];
    head[u]=etot;
}
void dfs1(int u,int fa)//第一遍求siz fat dep 重儿子 
{
    size[u]=1; 
    fat[u]=fa;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        dep[v]=dep[u]+1;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]||son[u]==-1) son[u]=v;
    }
}
void dfs2(int u,int tp)//第二遍求dfs序 求dfs序时先进入重儿子  top[]
{
    in[u]=++idc;
    seq[idc]=u;
    top[u]=tp;
    if(son[u]!=-1) dfs2(son[u],tp);
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v==son[u]||v==fat[u]) continue;
        dfs2(v,v);
    }
    out[u]=idc;
}
node *build(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg) {
        nd->cof=nd->cor=col[seq[lf]];
        nd->fg=-1;
        nd->num=1;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    nd->fg=-1;//!!!!!
    nd->update();
    return nd;
}
void modify(node *nd,int L,int R,int lf,int rg,int c)
{
    if(L<=lf&&rg<=R){
        nd->fg=c;
        nd->cof=c;
        nd->cor=c;
        nd->num=1;//!!!!
        return;
    }
    int mid=(lf+rg)>>1;
    nd->pushdown();
    if(L<=mid)   modify(nd->ls,L,R,lf,mid,c);
    if(R>mid) modify(nd->rs,L,R,mid+1,rg,c);
    nd->update();
}
void modify(int u,int v,int c)
{
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        modify(rt,in[top[u]],in[u],1,n,c);
        u=fat[top[u]];
    }
    if(dep[u]<dep[v]) swap(u,v);  
    modify(rt,in[v],in[u],1,n,c);
}
int query(node *nd,int L,int R,int lf,int rg)
{
    if(L<=lf&&rg<=R){
        if(L==lf) cet[0]=nd->cof;
        if(R==rg) cet[3]=nd->cor;
        return nd->num;
    }
    int mid=(lf+rg)>>1;
    nd->pushdown();
    int ans=0,r=-1,l=-1;
    if(L<=mid){
        ans+=query(nd->ls,L,R,lf,mid);
        l=nd->ls->cor;
    } 
    if(R>mid) {
        ans+=query(nd->rs,L,R,mid+1,rg);
        r=nd->rs->cof;
    }
    nd->update();

    if(l==r) ans--;
    return ans;
}
int query(int u,int v)
{
    int ans=0;
    memset(cet,-1,sizeof(cet));
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
            swap(cet[1],cet[2]);
        } 
        ans+=query(rt,in[top[u]],in[u],1,n);
        u=fat[top[u]];
        if(cet[3]==cet[1]) ans--;
        cet[1]=cet[0];
    }
    if(dep[u]<dep[v]) {
        swap(u,v);
        swap(cet[1],cet[2]);
    }
    ans+=query(rt,in[v],in[u],1,n);
    if(cet[1]==cet[3]) ans--;
    if(cet[2]==cet[0]) ans--;
    return ans;
}
int main()
{
    memset(son,-1,sizeof(son));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&col[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        adde(u,v),adde(v,u);
    }
    dfs1(1,1);
    dfs2(1,1);
    rt=build(1,idc);
    while(m--){
        char ch;
        int a,b,c;
        scanf("\n%c",&ch);
        if(ch=='C'){
            scanf("%d%d%d",&a,&b,&c);
            modify(a,b,c);
        }
        if(ch=='Q'){
            scanf("%d%d",&a,&b);
            printf("%d\n",query(a,b));
        }
    }
    return 0;
}

后记:突然发现博客里第一篇文章说的“想写但没过的树链剖分“就是这一道,缘分啊,真真妙不可言~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值