bzoj2589: Spoj 10707 Count on a tree II

Description

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

Input

第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v),表示一组询问。

数据范围是N<=40000 M<=100000 点权在int范围内

Output

M行,表示每个询问的答案。

1.每次在树上找到一棵树高为sqrt(n)的子树分为一块并删去,最后剩下树高不足sqrt(n)的部分(如果有)另成一块,这样树就被分为至多sqrt(n)块,且每块高度不超过sqrt(n)

2.对每个块的树根,预处理出这个点到树上所有点的路径的答案

3.对每个点,预处理出这个点到1号点的路径上每种点权出现的最大深度,用可持久化块状数组保存(数组分为sqrt(n)块,每个节点保存指向这些块的指针,修改时暴力复制所修改的块并修改)

4.对一个询问(u,v),若u,v在同一块则暴力,否则令u所在块的根的深度大于v所在块的根的深度,设x为u所在块的根,则x到v的答案已预处理好,根据第3步预处理的内容可以查询u到x的路径上(不包括x)的点权是否在x到v的路径上出现过从而得到答案

以上每一步均是O(n3/2)时间复杂度,常数非常大,可能需要一些常数优化

#include<bits/stdc++.h>
const int N=40017;
int n,m,B;
int read(){
    int x=0,c=getchar();
    while(c<48)c=getchar();
    while(c>47)x=x*10+c-48,c=getchar();
    return x;
}
int v[N],vs[N],e[N*2][2],e0[N],ep=2,la=0;
int id[N],rt[N],idp=0,fa[N],dep[N],t[N],ANS=0;
int sz[N],pf[N],top[N];
int ans[210][N],h[N];
int mem[261*N],*ptr=mem;
struct Array{
    int*arr[161];
    const int&operator[](int x){
        return arr[x>>8][x&255];
    }
    void copy(Array&src,int x,int y){
        memcpy(&arr,&src.arr,161*4);
        ptr+=261;
        memcpy(ptr,arr[x>>8],256*4);
        ptr[x&255]=y;
        arr[x>>8]=ptr;
    }
}as[N];
int lca(int x,int y){
    int a=top[x],b=top[y];
    while(a!=b){
        if(dep[a]<dep[b])std::swap(a,b),std::swap(x,y);
        x=fa[a];a=top[x];
    }
    return dep[x]<dep[y]?x:y;
}
int vio(int x,int y){
    int a=lca(x,y),r=0;
    for(int w=x;w!=a;w=fa[w])if(!t[v[w]]++)++r;
    for(int w=y;w!=a;w=fa[w])if(!t[v[w]]++)++r;
    if(!t[v[a]])++r;
    for(int w=x;w!=a;w=fa[w])t[v[w]]=0;
    for(int w=y;w!=a;w=fa[w])t[v[w]]=0;
    return r;
}
int query(int x,int y){
    if(id[x]==id[y])return vio(x,y);
    if(dep[rt[id[x]]]<dep[rt[id[y]]])std::swap(x,y);
    int d=dep[lca(x,y)],b=rt[id[x]];
    int r=ans[id[b]][y];
    for(int w=x;w!=b;w=fa[w]){
        int c=v[w];
        if(!t[c]&&as[b][c]<d&&as[y][c]<d)++r,t[c]=1;
    }
    for(int w=x;w!=b;w=fa[w])t[v[w]]=0;
    return r;
}
void f3(int w,int pa,int ID){
    if(!t[v[w]]++)++ANS;
    ans[ID][w]=ANS;
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=pa)f3(u,w,ID);
    }
    if(!--t[v[w]])--ANS;
}
void f1(int w,int pa){
    sz[w]=1;
    fa[w]=pa;
    dep[w]=dep[pa]+1;
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=pa){
            f1(u,w);
            sz[w]+=sz[u];
            if(sz[u]>sz[pf[w]])pf[w]=u;
        }
    }
}
void f2(int w){
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=fa[w]&&!id[u]){
            id[u]=id[w];
            f2(u);
        }
    }
}
void f4(int w){
    as[w].copy(as[fa[w]],v[w],dep[w]);
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=fa[w])f4(u);
    }
}
void f5(int w,int tp){
    top[w]=tp;
    if(pf[w])f5(pf[w],tp);
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=fa[w]&&u!=pf[w])f5(u,u);
    }
}
void f6(int w){
    h[w]=0;
    for(int i=e0[w];i;i=e[i][1]){
        int u=e[i][0];
        if(u!=fa[w]&&!id[u]){
            f6(u);
            if(h[u]>=h[w])h[w]=h[u]+1;
        }
    }
}int main(){
    n=read();m=read();
    B=sqrt(n+1)+1;
    for(int i=1;i<=n;i++)vs[i]=v[i]=read();
    std::sort(vs+1,vs+n+1);
    for(int i=1;i<=n;i++)v[i]=std::lower_bound(vs+1,vs+n+1,v[i])-vs;
    for(int i=1;i<n;i++){
        int a=read(),b=read();
        e[ep][0]=b;e[ep][1]=e0[a];e0[a]=ep++;
        e[ep][0]=a;e[ep][1]=e0[b];e0[b]=ep++;
    }
    for(int i=0;i<161;i++)as[0].arr[i]=mem;
    f1(1,0);
    do{
        f6(1);
        int r=1;
        for(int i=1;i<=n;i++)if(!id[i]&&h[i]==B){
            r=i;
            break;
        }
        rt[id[r]=++idp]=r;
        f2(r);
        f3(r,0,idp);
    }while(!id[1]);
    f4(1);f5(1,1);
    while(m--){
        int x=read()^la,y=read();
        printf("%d\n",la=query(x,y));
    }
    return 0;
}
时间: 2024-09-30 09:55:51

bzoj2589: Spoj 10707 Count on a tree II的相关文章

BZOJ 2589 Spoj 10707 Count on a tree II 强制在线莫队算法(TLE)

题目大意:给定一棵树,每个节点有一个颜色,多次询问某条路径上颜色数量,强制在线 正解是块状数组,强制在线莫队会TLE到死,想AC这道题的不用看了 如果朴素的跑树上莫队其实并不难- - 但是强制在线 因此我们可以考虑强制在线莫队算法 将树分成O(n^1/3)块,每块大小O(n^2/3) 记录每两块之间的答案.每种颜色的出现次数和哪些点被记录到了答案中 每次查询先找到两端点所在块的端点的答案,然后暴力用莫队转移即可 空间复杂度O(n^1/3)*O(n^1/3)*O(n)=O(n^5/3) 预处理时间

spoj COT2 - Count on a tree II

COT2 - Count on a tree II http://www.spoj.com/problems/COT2/ #tree You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight. We will ask you to perform the following operation: u v : ask for how many

SPOJ COT2 Count on a tree II(树上莫队)

题目链接:http://www.spoj.com/problems/COT2/ You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perfrom the following operation: u v : ask for how many different integers that repr

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

SPOJ.COT2 Count on a tree II(树上莫队)

题目链接(同上一题苹果树) 为什么第10个点T了一晚上.. 下面那个却AC了?跑的也不慢. TLE: /* 在DFS序做莫队 当一个点不是另一个点的LCA时,需要加上它们LCA的贡献 */ #include <cmath> #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() //#define gc() (SS==TT&&(TT=(SS

AC日记——Count on a tree II spoj

Count on a tree II 思路: 树上莫队: 先分块,然后,就好办了: 来,上代码: #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 40005 #define maxm 100005 struct QueryType { in

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问.

bzoj 2588: Spoj 10628. Count on a tree LCA+主席树

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数

BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一