【SPOJ10707】COT2 - Count on a tree II

题目大意:给定一棵 N 个节点的无根树,每个节点有一个颜色。现有 M 个询问,每次询问一条树链上的不同颜色数。

题解:学会了树上莫队。
树上莫队是将节点按照欧拉序进行排序,将树上问题转化成序列上的问题进行求解的算法。需要分两种情况进行讨论,第一种情况是对于询问 x,y 来说,x 为 y 的祖先,则询问的区间为 \(st[x],st[y]\),第二种情况是 x 与 y 处在两个不同的子树内,这时发现 \(lca(x,y)\) 的欧拉序并不在 [ed[x], st[y]] 内,因此需要额外考虑 lca 的贡献。

代码如下

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
#define cls(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=4e4+10;
const int maxm=1e5+10;
const double eps=1e-6;
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll sqr(ll x){return x*x;}
inline ll fpow(ll a,ll b,ll c){ll ret=1%c;for(;b;b>>=1,a=a*a%c)if(b&1)ret=ret*a%c;return ret;}
inline ll read(){
    ll x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}
/*------------------------------------------------------------*/

vector<int> G[maxn];
int f[maxn][20],dep[maxn],idfn[maxn<<1],st[maxn],ed[maxn],e_clk;
int n,m,a[maxn],d[maxn],tot,bsize;
struct query{int id,bl,l,r,lca;}q[maxm];
bool cmp(const query &x,const query &y){return x.bl!=y.bl?x.bl<y.bl:x.r<y.r;}
inline int get(int pos){return (pos-1)/bsize+1;}

void dfs(int u,int fa){
    st[u]=++e_clk,idfn[e_clk]=u;
    for(int i=1;i<=16;i++)f[u][i]=f[f[u][i-1]][i-1];
    for(auto v:G[u]){
        if(v==fa)continue;
        dep[v]=dep[u]+1,f[v][0]=u;
        dfs(v,u);
    }
    ed[u]=++e_clk,idfn[e_clk]=u;
}
int getlca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=16;~i;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    if(x==y)return x;
    for(int i=16;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}

void read_and_parse(){
    n=read(),m=read(),bsize=sqrt(2*n);
    for(int i=1;i<=n;i++)a[i]=d[i]=read();
    sort(d+1,d+n+1);
    tot=unique(d+1,d+n+1)-d-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(d+1,d+tot+1,a[i])-d;
    for(int i=1,x,y;i<n;i++){
        x=read(),y=read();
        G[x].pb(y),G[y].pb(x);
    }
    dep[1]=1,dfs(1,0);
    for(int i=1,x,y;i<=m;i++){
        x=read(),y=read();
        if(st[x]>st[y])swap(x,y);
        int lca=getlca(x,y);
        q[i].id=i;
        if(lca==x)q[i].l=st[x],q[i].r=st[y],q[i].bl=get(q[i].l);
        else q[i].l=ed[x],q[i].r=st[y],q[i].lca=lca,q[i].bl=get(q[i].l);
    }
    sort(q+1,q+m+1,cmp);
}

bool vis[maxn];
int l=1,r=0,now=0,cnt[maxn],ans[maxm];

inline void update(int pos){
    int u=idfn[pos];
    if(vis[u]){
        --cnt[a[u]];
        if(!cnt[a[u]])--now;
    }else{
        if(!cnt[a[u]])++now;
        ++cnt[a[u]];
    }
    vis[u]^=1;
}

void solve(){
    for(int i=1,l=1,r=0;i<=m;i++){
        while(r<q[i].r)update(++r);
        while(r>q[i].r)update(r--);
        while(l<q[i].l)update(l++);
        while(l>q[i].l)update(--l);
        if(q[i].lca)update(st[q[i].lca]);
        ans[q[i].id]=now;
        if(q[i].lca)update(st[q[i].lca]);
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
int main(){
    read_and_parse();
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10664569.html

时间: 2024-11-08 02:33:30

【SPOJ10707】COT2 - Count on a tree II的相关文章

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

SPOJ10707 COT2 - Count on a tree II 【树上莫队】

题目分析: 考虑欧拉序,这里的欧拉序与ETT欧拉序的定义相同而与倍增LCA不同.然后不妨对于询问$u$与$v$让$dfsin[u] \leq dfsin[v]$,这样对于u和v不在一条路径上,它们可以改成询问$dfsin[u]$到$dfsin[v]$.否则改成$dfsout[u]$到$dfsin[v]$,并加上LCA上的影响,如果在询问过程中没有加入就加入,否则删除. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const

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

SPOJ:COT2 Count on a tree II

题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; const int _(1e5 + 5); typedef long long ll; I

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\) 转移

【LeetCode】Minimum Depth of Binary Tree 二叉树的最小深度 java

[LeetCode]Minimum Depth of Binary Tree Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. 递归和非递归,此提比较简单.广度优先遍历即可.关键之处就在于如何保持访问深度. 下面是4种代码: 1

【DataStructure】Description and Introduction of Tree

[Description] At ree is a nonlinear data structure that models a hierarchical organization. The characteristic eatures are that each element may have several successors (called its "children") and every element except one (called the "root&