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;

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, m, first[_], cnt, w[_], o[_], len;
int dfn[_], st[20][_], lg[_], deep[_], idx, fa[_];
int S[_], bl[_], blo, num, ans[_], sum[_], vis[_], Ans;
struct Edge{
    int to, next;
} edge[_];
struct Query{
    int l, r, id;

    IL int operator <(RG Query B) const{
        return bl[l] == bl[B.l] ? dfn[r] < dfn[B.r] : bl[l] < bl[B.l];
    }
} qry[_];

IL void Add(RG int u, RG int v){
    edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
}

IL void Dfs(RG int u){
    dfn[u] = ++idx, st[0][idx] = u; RG int l = S[0];
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(dfn[v]) continue;
        deep[v] = deep[u] + 1, fa[v] = u;
        Dfs(v);
        if(S[0] - l >= blo) for(++num; S[0] != l; --S[0]) bl[S[S[0]]] = num;
        st[0][++idx] = u;
    }
    S[++S[0]] = u;
}

IL void Chk(RG int &x, RG int u, RG int v){
    x = deep[u] < deep[v] ? u : v;
}

IL int LCA(RG int u, RG int v){
    u = dfn[u], v = dfn[v];
    if(u > v) swap(u, v);
    RG int log2 = lg[v - u + 1], t;
    Chk(t, st[log2][u], st[log2][v - (1 << log2) + 1]);
    return t;
}

IL void Update(RG int x){
    if(vis[x]) --sum[w[x]], Ans -= (!sum[w[x]]);
    else Ans += (!sum[w[x]]), ++sum[w[x]];
    vis[x] ^= 1;
}

IL void Modify(RG int u, RG int v){
    while(u != v){
        if(deep[u] > deep[v]) swap(u, v);
        Update(v), v = fa[v];
    }
}

int main(RG int argc, RG char* argv[]){
    len = n = Input(), m = Input(), blo = sqrt(n);
    for(RG int i = 1; i <= n; ++i) o[i] = w[i] = Input(), first[i] = -1;
    sort(o + 1, o + len + 1), len = unique(o + 1, o + len + 1) - o - 1;
    for(RG int i = 1; i <= n; ++i) w[i] = lower_bound(o + 1, o + len + 1, w[i]) - o;
    for(RG int i = 1, u, v; i < n; ++i)
        u = Input(), v = Input(), Add(u, v), Add(v, u);
    Dfs(1);
    if(S[0]) for(++num; S[0]; --S[0]) bl[S[S[0]]] = num;
    for(RG int i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
    for(RG int j = 1; j <= lg[idx]; ++j)
        for(RG int i = 1; i + (1 << j) - 1 <= idx; ++i)
            Chk(st[j][i], st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    for(RG int i = 1; i <= m; ++i){
        qry[i] = (Query){Input(), Input(), i};
        if(dfn[qry[i].l] > dfn[qry[i].r]) swap(qry[i].l, qry[i].r);
    }
    sort(qry + 1, qry + m + 1);
    RG int lca = LCA(qry[1].l, qry[1].r);
    Modify(qry[1].l, qry[1].r);
    Update(lca), ans[qry[1].id] = Ans, Update(lca);
    for(RG int i = 2; i <= m; ++i){
        Modify(qry[i - 1].l, qry[i].l), Modify(qry[i - 1].r, qry[i].r);
        lca = LCA(qry[i].l, qry[i].r);
        Update(lca), ans[qry[i].id] = Ans, Update(lca);
    }
    for(RG int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/8726817.html

时间: 2024-10-27 07:45:24

SPOJ: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

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

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

bzoj2589【 Spoj 10707】 Count on a tree II

题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. 输入格式 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v),表示一组询问. 数据范围是N<=40000 M<=100000 点权在int范围内 输出格式 M行,

【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]] 内,因此需要额外考虑 lc

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

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