洛谷P2633 Count on a tree

题目描述

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

输入输出格式

输入格式:

第一行两个整数N,M。

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

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制

8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2

输出样例#1: 复制

2
8
9
105
7

说明

HINT:

N,M<=100000

暴力自重。。。

来源:bzoj2588 Spoj10628.

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。

树上第$k$大问题

做法比较套路

首先对权值离散化。

然后对每个节点建主席树,从父亲那里拷贝历史版本

求LCA的话用树剖,顺便可以建主席树。

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2*1e6+10;
const int INF=1e8+10;
inline char nc()
{
    static char buf[MAXN],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    char c=nc();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=nc();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=nc();}
    return x*f;
}
struct Edge
{
    int u,v,nxt;
}E[MAXN];
int head[MAXN];
int num=1;
inline void AddEdge(int x, int y)
{
    E[num].u = x;
    E[num].v = y;
    E[num].nxt = head[x];
    head[x] = num++;
}
int a[MAXN], b[MAXN], N, M, root[MAXN], totnum;
int deep[MAXN], son[MAXN], top[MAXN], fa[MAXN], tot[MAXN];
struct node
{
    int ls, rs, w;
}T[MAXN];
void Insert(int pre,int &now,int ll,int rr,int val)
{
    now=++totnum;
    T[now].ls=T[pre].ls;T[now].rs=T[pre].rs;
    T[now].w=T[pre].w+1;
    if(ll==rr) return ;
    int mid=ll+rr>>1;
    if(val<=mid) Insert(T[pre].ls,T[now].ls,ll,mid,val);
    else         Insert(T[pre].rs,T[now].rs,mid+1,rr,val);
}
void dfs1(int now, int f)
{
    fa[now] = f;
    Insert(root[f],root[now],1,N,a[now]);
    tot[now] = 1;
    int maxson=-1;
    for(int i = head[now]; i != -1; i = E[i].nxt)
    {
        if(deep[E[i].v] == 0 && E[i].v != f)
        {
            deep[E[i].v] = deep[now] + 1;
            dfs1(E[i].v, now);
            tot[now] += tot[E[i].v];
            if(tot[E[i].v]>maxson) maxson = tot[E[i].v],son[now] = E[i].v;
        }
    }
}
void dfs2(int now, int topf)
{
    top[now] = topf;
    if(!son[now]) return ;
    dfs2(son[now], topf);
    for(int i = head[now]; i != -1; i=E[i].nxt)
        if(E[i].v != son[now] && E[i].v != fa[now])
            dfs2(E[i].v, E[i].v);
}
int LCA(int x, int y)
{
    while(top[x] != top[y])
    {
        if(deep[top[x]] < deep[top[y]]) swap(x,y);
        x = fa[top[x]];
    }
    if(deep[x] > deep[y]) swap(x,y);
    return x;
}
int Query(int x, int y, int lca, int falca, int ll, int rr, int k)
{
    if(ll==rr) return ll;
    int used=T[T[x].ls].w + T[T[y].ls].w - T[T[lca].ls].w - T[T[falca].ls].w;
    int mid=ll+rr>>1;
    if(k<=used) return Query(T[x].ls, T[y].ls, T[lca].ls, T[falca].ls, ll, mid, k);
    else        return Query(T[x].rs, T[y].rs, T[lca].rs, T[falca].rs, mid+1, rr, k-used);
}
int main()
{
    #ifdef WIN32
    freopen("a.in","r",stdin);
    #else
    #endif
    memset(head,-1,sizeof(head));
    N = read(); M = read();
    for(int i = 1; i<=N; i++) a[i] = b[i] = read();
    sort(b+1, b+N+1);
    int num = unique(b, b+N+1) - b - 1;
    for(int i = 1; i<=N; i++) a[i]=lower_bound(b, b+N, a[i]) - b;
    for(int i=1; i<=N-1; i++)
    {
        int x=read(), y=read();
        AddEdge(x, y);
        AddEdge(y, x);
    }
    deep[1] = 1;
    dfs1(1, 0);
    dfs2(1,1);
    int lastans=0;
    while(M--)
    {
        int x=read(),y=read(),k=read();
        x=x^lastans;
        int lca=LCA(x,y);
        lastans=b[Query(root[x], root[y], root[lca], root[fa[lca]],1,N,k)];
        printf("%d\n",lastans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zwfymqz/p/8439539.html

时间: 2024-11-02 07:31:50

洛谷P2633 Count on a tree的相关文章

[luogu P2633] Count on a tree

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

洛谷P3018 [USACO11MAR]树装饰Tree Decoration

洛谷P3018 [USACO11MAR]树装饰Tree Decoration树形DP 因为要求最小,我们就贪心地用每个子树中的最小cost来支付就行了 1 #include <bits/stdc++.h> 2 #define For(i, j, k) for(int i=j; i<=k; i++) 3 #define Dow(i, j, k) for(int i=j; i>=k; i--) 4 #define LL long long 5 using namespace std;

洛谷P2633 王后万岁

题目描述 byteland的王后深受百姓爱戴.为了表达他们的爱,国民们打算占领一个新的国家,并以王后的名字命名.这个国家有n座城市.城市之间有双向道路连接,且每两个城市之间有且仅有一条道路.每座城市对其拥有者来说都有一定的收益.尽管国民们非常爱戴他们的王后,他们并不一定会征服所有的城市献给她.他们只想占领一部分城市(至少有一座),这些城市必须满足两个条件:所有被占领的城市相互间必须是连通的,且城市收益之和最大.你的任务就是算出最大收益是多少. 输入输出格式 输入格式: 第一行是城市的数量n(1<

P2633 Count on a tree(主席树)

题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. 输入输出格式 输入格式: 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问. 输出格式: M行,表示每个询问的答案. 输入输出样例 输入样例#1:

P2633 Count on a tree(树上主席树)

思路 运用树上差分的思想,转化成一个普通的主席树模型即可求解 代码 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; struct Node{ int lson,rson,sz; }pt[100100*30]; const int MAXlog=19; int dep[100100],jump[100100][MAXlog],lastans=0,n,m,u[100

P2633 Count on a tree 树上主席树

题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. 输入格式 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问. 输出格式 M行,表示每个询问的答案. 输入输出样例 输入 #1复制 8 5 105

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

VIjos——V 1782 借教室 | | 洛谷——P1083 借教室

https://vijos.org/p/1782|| https://www.luogu.org/problem/show?pid=1083 描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题.我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示

洛谷 P2486 BZOJ 2243 [SDOI2011]染色

题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. 输入输出格式 输入格式: 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边. 下面 行每行描述一个操作: “C a