Count on a tree II(树上莫队)

Count on a tree II(luogu)

Description

题目描述

给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。

输入格式

第一行有两个整数n和m(n=40000,m=100000)。

第二行有n个整数。第i个整数表示第i个节点表示的整数。

在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。

在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。

输出格式

对于每个询问,输出结果。

Solution

树上莫队模板

先按照欧拉序(两次访问的时间,此处不赘述了)对树上的节点分块

当询问为x,y时(设st[x]<st[y])

(i)x为y的祖先

访问区间为st[x]-st[y]

(ii)x不为y的祖先

访问区间为ed[x]-st[y]

解释:

  • 因为x,y都在lca的子树上且x比y先被访问,所以访问完x回溯后再访问y,即ed[x]<st[y]
  • 先将x-lca上的点访问一遍(ed[x]-ed[lca至x路径上的son]),再将lca至y上的点访问一遍(st[lca至y路径上的son]-st[y])
  • 由于st[lca]<ed[x] && st[y]<ed[lca],所以lca没有被算进去,要特判

还有一件特别重要的事:

这题非要用树剖求lca!!!(zz,卡我好久...)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N=1e5+10,M=4e4+10;
int ans[N],an,s_d[M],s_u[M],u,v,n,m,d[M],cnt,tot,pos[N],st[M],ed[M],si,bl,fa[M],dep[M],b[N];
int top[M],son[M],siz[M];
vector <int> link[N];
struct node
{
    int l,r,id,lca;
    bool operator <(const node &o)const
    {
        return pos[l]^pos[o.l]?l<o.l:(pos[l]&1?r<o.r:r>o.r);
    }
}q[N];
struct mode
{
    int v,id;
    bool operator <(const mode &o)const
    {
        return v<o.v;
    }
}a[M];
void dfs(int u,int fx)
{
    st[u]=++cnt,b[cnt]=u;
    fa[u]=fx,dep[u]=dep[fx]+1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v!=fx) dfs(v,u);
    }
    ed[u]=++cnt,b[cnt]=u;
}
void dfs1(int u)
{
    siz[u]=1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v==fa[u]) continue;
        dfs1(v),siz[u]+=siz[v];
        if(son[u]==0 || siz[v]>siz[son[u]]) son[u]=v;
     }
}
void dfs2(int u)
{
    if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
    else return ;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v==fa[u] || v==son[u]) continue;
        top[v]=v,dfs2(v);
    }
}
int Lca(int x,int y)
{
    int px=top[x],py=top[y];
    while(px!=py)
        if(dep[px]>dep[py])
            x=fa[px],px=top[x];
        else y=fa[py],py=top[y];
    if(dep[x]>dep[y]) return y;
    else return x;
}
void get(int x,int y)
{
    if(x==0) return;
    s_u[x]+=y;
    if(s_u[x]&1)
    {
        s_d[d[x]]++;
        if(s_d[d[x]]==1) an++;
    }
    else
    {
        s_d[d[x]]--;
        if(s_d[d[x]]==0) an--;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].v),a[i].id=i;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        if(i==1 || a[i].v!=a[i-1].v) cnt++;
        d[a[i].id]=cnt;
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        link[u].push_back(v);
        link[v].push_back(u);
    }
    cnt=0,dfs(1,0);
    dfs1(1),top[1]=1,dfs2(1);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);
        if(st[u]>st[v]) swap(u,v);
        int w=Lca(u,v);
        if(w==u) q[i].l=st[u],q[i].r=st[v],q[i].lca=0;
        else q[i].lca=d[w],q[i].l=ed[u],q[i].r=st[v];
        q[i].id=i;
    }
    n=cnt;
    si=sqrt(n);
    bl=(n+si-1)/si;
    for(int i=1;i<=bl;i++)
    {
        int z=min(n,i*si);
        for(int j=(i-1)*si+1;j<=z;j++)
            pos[j]=i;
    }
    sort(q,q+m);
    int nl=0,nr=0;
    for(int i=0;i<m;i++)
    {
        while(nl>q[i].l) get(b[--nl],1);
        while(nl<q[i].l) get(b[nl++],-1);
        while(nr>q[i].r) get(b[nr--],-1);
        while(nr<q[i].r) get(b[++nr],1);
        ans[q[i].id]=an;
        if(q[i].lca) ans[q[i].id]+=(s_d[q[i].lca]==0);
    }
    for(int i=0;i<m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/hsez-cyx/p/12262871.html

时间: 2024-11-09 16:41:29

Count on a tree II(树上莫队)的相关文章

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

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

参考博客 对于树上的路径询问问题 O(1)的时间加入或删除一个点的贡献 -> \(O(n\sqrt n)\)的复杂度求出所有询问的答案 对树上的结点进行分块,离线询问后排序,顺序遍历暴力转移路径(转移时加入或删除路径上的点的贡献即可). 关于转移路径:首先定义路径:设\(T_u\)为\(u\) 到根的路径上边的集合,那么\(u\)到\(v\) 的路径上的边的集合就是\(T_u \triangle T_v\) (\(\triangle\) 是对称差).要从\(u\rightarrow v\) 转移

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序作为第二关键字

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

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

「SPOJ10707」Count on a tree II

「SPOJ10707」Count on a tree II 传送门 树上莫队板子题. 锻炼基础,没什么好说的. 参考代码: #include <algorithm> #include <cstdio> #include <cmath> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w

[SPOJ10707]Count on a tree II

试题描述 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 different integers that represent the weight of nodes there a

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了. 关于树链的滑动,只需要特殊处理一下$LCA$就行了. 在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数.因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$. 转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移. 这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫