BZOJ_P3757 苹果树(树上莫队+LCA)

48 篇文章 0 订阅
7 篇文章 0 订阅

BZOJ传送门

Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 1221 Solved: 476
[Submit][Status][Discuss]
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
M<=100000

Source

Sol:
非常简单的树上莫队,跑得好慢QuQ
真是受不了出题人,对0的描述不全面QuQ(还是我太sb了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
#define N 50005
#define K 255
#define M 100005
inline int in(int x=0,char ch=getchar()){while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x;}
struct Q{int u,v,a,b,id;}q[M];int ut[N];
int stk[M],c[N],a[N],w[N],block[N];int n,m,cnt,Lim,top,ans;
int f[N][17],d[N];bool v[N];
inline void Add_Block(int size){cnt++;while(size--) block[stk[top--]]=cnt;}
struct Tree{
    vector<int> g[N];
    int dfs(int u,int p,int s=0){
        for(int i=0,v,lim=g[u].size();i<lim;i++){
            if((v=g[u][i])==p) continue;
            f[v][0]=u,d[v]=d[u]+1,s+=dfs(v,u);
            if(s>=Lim) Add_Block(s),s=0;
        }return stk[++top]=u,s+1;
    }
    void work(){for(int i=1;i<=16;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];}
    inline int LCA(int u,int v){
        if(d[u]<d[v]) swap(u,v);int l=d[u]-d[v];
        if(l) for(int i=0;i<=16;++i) if(l&(1<<i)) u=f[u][i];
        if(u==v) return u;
        for(int i=16;~i;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
        return f[u][0];
    }
}t;
struct Mo{
    inline void Updata(int u){
        if(!v[u]&&!(c[a[u]]++)) ans++;
        if(v[u]&&!(--c[a[u]])) ans--;
        v[u]^=1;
    }
    inline void Move(int u,int v){
        if(d[u]<d[v]) swap(u,v);
        while(d[u]!=d[v]) Updata(u),u=f[u][0];
        while(u!=v){Updata(u),u=f[u][0];Updata(v),v=f[v][0];}
    }
    void Solve(){
        int x,y;x=y=1;ans=0;
        for(int i=1;i<=m;i++){
            Move(x,q[i].u),x=q[i].u,Move(y,q[i].v),y=q[i].v;
            int lca=t.LCA(x,y);
            if(lca) Updata(lca);
            ut[q[i].id]=ans-(q[i].a!=q[i].b&&c[q[i].a]&&c[q[i].b]);
            if(lca) Updata(lca);
        }
    }
}W;
int cmp(Q a,Q b){return block[a.u]<block[b.u]||(block[a.u]==block[b.u]&&block[a.v]<block[b.v]);}
void init(){
    n=in(),m=in();int u,v;for(int i=1;i<=n;i++) a[i]=in();Lim=max(1,(int)sqrt(n*2+0.5));
    for(int i=1;i<=n;i++){
        u=in(),v=in();if(u&&v) t.g[u].push_back(v),t.g[v].push_back(u);
    }
    f[1][0]=1;t.dfs(1,1);t.work();
    for(int i=1;i<=m;i++){
        q[i].u=in(),q[i].v=in(),q[i].a=in(),q[i].b=in(),q[i].id=i;
        if(block[q[i].u]>block[q[i].v]) swap(q[i].u,q[i].v);}
    sort(q+1,q+m+1,cmp);W.Solve();
    for(int i=1;i<=m;i++) printf("%d\n",ut[i]);
}
int main(){init();return 0;}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值