[树上莫队]JZOJ 3360 苹果树

Description


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



有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为N的苹果出发,由树枝走到编号为N的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果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
 
Data Constraint

分析

题拿到手

第一秒:又链剖?

两分钟后:树上莫队

然鹅并没有学,打了个链剖拿mutliset维护颜色集合结果没对拍爆零了(貌似可以水到20)

新学的东西会写学习笔记解析的,这里不做赘述(毕竟算个模板题)

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=5e4+10;
struct Graph {
    int v,nx;
}g[2*N];
int cnt,list[N];
struct Task {
    int x,y,l,r,lca,id,c1,c2;
}t[2*N];
int l[N],r[N],f[N][20],dep[N],id[2*N],ans[2*N],c[N],a[N],q[2*N];
bool in[N];
int n,m,mlen,tme,num;

void Add(int u,int v) {
    g[++cnt]=(Graph){v,list[u]};list[u]=cnt;
    g[++cnt]=(Graph){u,list[v]};list[v]=cnt;
}

void DFS(int u,int fa) {
    dep[u]=dep[fa]+1;f[u][0]=fa;q[l[u]=++tme]=u;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) DFS(g[i].v,u);
    q[r[u]=++tme]=u;
}

int LCA(int x,int y) {
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=19;i>=0;i--)
        if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    if (x==y) return x;
    for (int i=19;i>=0;i--)
        if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

bool CMP(Task a,Task b) {
    return id[a.l]<id[b.l]||id[a.l]==id[b.l]&&((id[a.l]&1)?a.r<b.r:a.r>b.r);
}

void Deal(int x) {
    in[x]^=1;
    if (in[x]) {
        if (!c[a[x]]) num++;
        c[a[x]]++;
    }
    else {
        c[a[x]]--;
        if (!c[a[x]]) num--;
    }
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1,u,v;i<=n;i++) {
         scanf("%d%d",&u,&v);
         if (u&&v) Add(u,v);
    }
    DFS(1,0);
    for (int j=1;j<20;j++)
        for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    for (int i=1;i<=m;i++) {
        scanf("%d%d%d%d",&t[i].x,&t[i].y,&t[i].c1,&t[i].c2);t[i].id=i;
        if (l[t[i].x]>l[t[i].y]) swap(t[i].x,t[i].y);
        if (l[t[i].y]<=r[t[i].x]) t[i].l=l[t[i].x],t[i].r=l[t[i].y],t[i].lca=0;
        else t[i].l=r[t[i].x],t[i].r=l[t[i].y],t[i].lca=1;
    }
    mlen=sqrt(tme);
    for (int i=1;i<=tme;i++) id[i]=i/mlen+(i%mlen!=0);
    sort(t+1,t+m+1,CMP);
    int l=1,r=1;Deal(q[1]);
    for (int i=1;i<=m;i++) {
        while (l<t[i].l) Deal(q[l++]);while (l>t[i].l) Deal(q[--l]);
        while (r<t[i].r) Deal(q[++r]);while (r>t[i].r) Deal(q[r--]);
        int lca=LCA(t[i].x,t[i].y);
        if (t[i].lca) Deal(lca);
        ans[t[i].id]=num;
        if (t[i].c1!=t[i].c2&&c[t[i].c1]&&c[t[i].c2]) ans[t[i].id]--;
        if (t[i].lca) Deal(lca);
    }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
View Code

 

转载于:https://www.cnblogs.com/mastervan/p/11178380.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值