bzoj4771 -- dfs序+倍增+主席树

先考虑没有深度限制的情况。

先将每个节点的权值设为1,对于颜色相同且在dfs序中最近的2个点,用倍增求出lca并将它的权值减一。然后子树中不同的颜色种数就是子树的权值和了。

有深度限制时,考虑以深度为时间建立主席树。

将每个点按深度排序,枚举一遍。对每种颜色开一个set,枚举到一个点时将它在dfs序中的位置加入set中并更新就可以了。

时间复杂度O(T*(nlogn+mlogn))

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2)return EOF;
    }
    return *p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<‘0‘||c>‘9‘;c=nc());
    for(x=0;c>=‘0‘&&c<=‘9‘;x=(x<<3)+(x<<1)+c-48,c=nc());
}
#define N 100010
#define M 20
#define I set<Node3>::iterator
struct Node1{
    int w,l,r;
}a[N*M<<2];
struct Edge{
    int t,nx;
}e[N];
int x,y,Num,i,j,k,n,T,Cnt,m,d[N],Rt[N],l[N],r[N],h[N],c[N],f[N][M],Last;
struct Node3{
    int f,w;
    Node3(){}
    Node3(int f,int w):f(f),w(w){}
    bool operator < (Node3 x)const{return w<x.w;}
}A[N],Tmp;
set<Node3>s[N];
inline int _Min(int x,int y){return x<y?x:y;}
inline void Add(int x,int y){e[++Num].t=y;e[Num].nx=h[x];h[x]=Num;}
inline void Dfs(int x){
    d[x]=d[f[x][0]]+1;l[x]=++Num;
    for(int i=h[x];i;i=e[i].nx)Dfs(e[i].t);
    r[x]=Num;
}
inline void Build(int& Node,int l,int r){
    Node=++Cnt;a[Node].w=0;
    if(l==r)return;
    int Mid=l+r>>1;
    Build(a[Node].l,l,Mid);
    Build(a[Node].r,Mid+1,r);
}
inline void Insert(int L,int& Node,int x,int l,int r,int y){
    Node=++Cnt;a[Node]=a[L];
    a[Node].w+=y;
    if(l==r)return;
    int Mid=l+r>>1;
    if(x<=Mid)Insert(a[L].l,a[Node].l,x,l,Mid,y);else Insert(a[L].r,a[Node].r,x,Mid+1,r,y);
}
inline int Query(int Node,int l,int r,int L,int R){
    if(Node==0||l>R||r<L)return 0;
    if(l>=L&&r<=R)return a[Node].w;
    int Mid=l+r>>1;
    return Query(a[Node].l,l,Mid,L,R)+Query(a[Node].r,Mid+1,r,L,R);
}
inline int Lca(int x,int y){
    if(d[x]<d[y])swap(x,y);
    for(int i=M-1;i>=0;i--)
    if(d[f[x][i]]>=d[y])x=f[x][i];
    if(x==y)return x;
    for(int i=M-1;i>=0;i--)
    if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int main()
{
    Read(T);
    while(T--){
        Read(n);Read(m);
        for(i=1;i<=n;i++)Read(c[i]);memset(h,0,sizeof(h));
        for(Num=Cnt=0,i=2;i<=n;i++)Read(f[i][0]),Add(f[i][0],i);
        for(j=1;j<M;j++)
        for(i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
        Num=0;Dfs(1);
        for(i=1;i<=n;i++)A[i].f=i,A[i].w=d[i];
        sort(A+1,A+n+1);
        for(i=1;i<=n;i++)s[i].clear();
        Build(Rt[1],1,Num);Insert(Rt[1],Rt[1],l[1],1,Num,1);s[c[1]].insert(Node3(1,l[1]));
        for(i=2;i<=n;i++){
            x=A[i-1].f;y=A[i].f;
            Insert(Rt[d[x]],Rt[d[y]],l[y],1,Num,1);
            if(s[c[y]].empty())s[c[y]].insert(Node3(y,l[y]));else{
                s[c[y]].insert(Node3(y,l[y]));
                I z=s[c[y]].find(Node3(y,l[y]));
                I P=--z;z=s[c[y]].find(Node3(y,l[y]));I S=++z;
                if(P!=s[c[y]].end())Insert(Rt[d[y]],Rt[d[y]],l[Lca(P->f,y)],1,Num,-1);
                if(S!=s[c[y]].end())Insert(Rt[d[y]],Rt[d[y]],l[Lca(S->f,y)],1,Num,-1);
                if(P!=s[c[y]].end()&&S!=s[c[y]].end())Insert(Rt[d[y]],Rt[d[y]],l[Lca(P->f,S->f)],1,Num,1);
            }
        }
        Last=0;
        while(m--){
            Read(x);Read(y);x^=Last;y^=Last;
            Last=Query(Rt[_Min(d[x]+y,A[n].w)],1,Num,l[x],r[x]);
            printf("%d\n",Last);
        }
    }
    return 0;
}

bzoj4771

时间: 2024-10-29 00:34:48

bzoj4771 -- dfs序+倍增+主席树的相关文章

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

Codeforces Round #200 (Div. 1) D. Water Tree(dfs序加线段树)

思路: dfs序其实是很水的东西.  和树链剖分一样, 都是对树链的hash. 该题做法是:每次对子树全部赋值为1,对一个点赋值为0,查询子树最小值. 该题需要注意的是:当我们对一棵子树全都赋值为1的时候, 我们要查询一下赋值前子树最小值是不是0, 如果是的话, 要让该子树父节点变成0, 否则变0的信息会丢失. 细节参见代码: #include <cstdio> #include <cstring> #include <algorithm> #include <i

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>

Luogu P2982 [USACO10FEB]慢下来 Slowing down | dfs序、线段树

题目链接 题目大意: 有一棵N个结点树和N头奶牛,一开始所有奶牛都在一号结点,奶牛们将按从编号1到编号N的顺序依次前往自己的目的地,求每头奶牛在去往自己目的地的途中将会经过多少已经有奶牛的结点. 题解: 可以发现,每一头奶牛到达目的地后,都只会对还未到达目的地的奶牛中,目的地在它目的地子树中的奶牛的答案产生贡献. 比如说在下面这棵树中,一头奶牛到达了图中深色结点,那么在还未到达目的地的奶牛中,只有目的地在深色结点子树中的奶牛才会由深色结点对答案产生贡献. 所以,我们可以在每头奶牛到达目的地后,将

【手动开栈】【dfs序】【树状数组】【Tarjan】bzoj2819 Nim

考虑树状数组区间修改(只对其子树的答案有影响)点查询,每个点记录的是它到根路径上的权值异或和. 答案时query(L)^query(R)^a[lca]. 这种方法在支持区间加法.减法的树上询问的时候可以避免树链剖分. 可能爆栈,考虑手动开栈.(诶诶Tarjan预处理lca的时候怎么没手动开栈?不要在意^_^) 实际上不会爆的. #include<cstdio> #include<stack> #include<algorithm> #include<queue&g

URAL 1890 . Money out of Thin Air (dfs序hash + 线段树)

题目链接: URAL 1890 . Money out of Thin Air 题目描述: 给出一个公司里面上司和下级的附属关系,还有每一个人的工资,然后有两种询问: 1:employee x y z ,如果编号为x的员工如果工资小于y,就给他加薪z. 2:department x y z ,如果编号为x的员工所管辖的范围内(包括自己),所有员工的工资平均数小于y,给该范围加薪z. 问q次操作后这个公司内每个员工的工资为多少? 解题思路: 根据上司和下级的附属关系,可以先建一个有向图,然后对有向

【dfs序】【树状数组】bzoj1103 [POI2007]大都市meg

预处理出每个点到根节点的土路数,插到一个树状数组里,然后每次修改只会对子树中的节点造成影响,于是相当于区间修改.点查询了. #include<cstdio> using namespace std; #define N 250001 int n,en,v[N<<1],next[N<<1],first[N],m; void AddEdge(const int &U,const int &V) { v[++en]=V; next[en]=first[U];

hdu4366 Successor (dfs序+zkw线段树)

Successor Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2559    Accepted Submission(s): 613 Problem Description Sean owns a company and he is the BOSS.The other Staff has one Superior.every st

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;