codechef T6 Pishty and tree dfs序+线段树

PSHTTR: Pishty 和城堡
题目描述 Pishty 是生活在胡斯特市的一个小男孩。胡斯特是胡克兰境内的一个古城,以其中世纪风格 的古堡和非常聪明的熊闻名全国。 胡斯特的镇城之宝是就是这么一座古堡,历史上胡斯特依靠这座古堡抵挡住了疯人国的大军。 对于 Pishty 来说,真正吸引他的是古堡悠长的走廊和高耸的钟楼,以及深藏于其中的秘密…… 古堡可以用一棵 N 个节点的树的描述,树中有 N ?1 条无向边,每条边有一个魔法数字 C。 当一个旅游团参观古堡时,他们会选择树上 U 到 V 的路径游览。他们认为,如果一条边的魔 法数字不超过 K,那么这条边是有趣的。而一条路径的吸引力就是路径上所有有趣的边的魔法数 字的异或和。 胡克兰的国王希望大力发展旅游业,因此他下令求出所有旅游团的游览路径的吸引力。而 Pishty立志成为国王身边的骑士,便自告奋勇承担了这一任务。但旅游团实在太多了,他也算不过 来。所以,请你帮Pishty解决这一问题:给定 M 个旅游团的旅游路径,请你求出路径的吸引力。
输入格式
输入的第一行包含一个整数 T,代表测试数据的组数。接下来是 T 组数据。 每组数据的第一行包含一个整数 N,代表树的节点个数。 接下来 N ?1 行,每行描述一条边。每行包含三个整数 U,V,C,代表节点 U 和 V 之间连有 一条魔法数字为 C 的边。 接下来一行包含一个整数 M,代表旅游团的数量。 接下来 M 行,每行包含三个整数 U,V,K,描述一个旅游团。
输出格式
对于每个旅游团,输出一行,包含一个整数,代表其路径的吸引力。
数据范围和子任务 ? 1 ≤ T ≤ 5 ? 1 ≤ N,M ≤ 105
? 1 ≤ U,V ≤ N ? 1 ≤ C,K ≤ 109
子任务 1(10 分): ? 1 ≤ N,M ≤ 10
子任务 2(20 分): ? 1 ≤ N,M ≤ 103
子任务 3(70 分): ? 无附加限制
样例数据
输入

1

5

1 2 1

2 3 2

2 4 5

3 5 10
6

5 4 5

5 4 10

5 4 1

1 2 1

4 1 10

1 5 8

输出

7

13

0

1

4

3

首先我们要了解异或的三个性质

1.a^b=b^a
2.(a^b)^c=a^(b^c)
3.a^a=0

这样之后我们可以发现 两个点之间路径的异或和可以转换 成两个点到根节点的异或和的值 异或起来就是答案

我们可以强制一为根 然后根据dfs序记录每个点包含的区间(也就是他在树上的子树范围) 方便做区间处理

然后我们可以离线处理答案 把答案按限制大小从小到达排一波 每次把符合的边插进去 找到这条边两个端点深度较深的那个点

注意一定是深度较深 也就是在树上位置较下边的点 这个画图很容易证明

然后把他包含的区间(也就是树上他的子树)进行一波区间异或也就是lazy 然后单点查询就可以啦

这道题调了很久 最后发现是sort的时候反向边的相对位置发生了改变 mdzz

最后贴一波代码咯

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=1<<17;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();}
    return ans*f;
}
int T,n,m;
int L,R,W,top;
int cnt,cnt2,first[M],deep[M];
struct node{int to,from,next,w;}e[2*M],e1[2*M];
bool cmp1(node a,node b){return a.w>b.w;}
void ins(int a,int b,int w){cnt++; e[cnt].from=a; e[cnt].to=b; e[cnt].w=w; e[cnt].next=first[a]; first[a]=cnt;}
void insert(int a,int b,int w){ins(a,b,w); ins(b,a,w);}
struct note{int u,v,w,pos;}q[M];
bool cmp2(note a,note b){return a.w<b.w;}
int l[M],r[M],sum;
int v[2*M],ans[M];
void prepare(){
    cnt2=cnt=0; memset(first,0,sizeof(first));
    L=0; R=0; W=0;
    sum=0; memset(v,0,sizeof(v));
    memset(deep,0,sizeof(deep)); deep[1]=1;
}
void dfs(int x){
    l[x]=++sum;
    for(int i=first[x];i;i=e[i].next){
        int now=e[i].to;
        if(!deep[now]) deep[now]=deep[x]+1,dfs(now);
    }
    r[x]=sum;
}
void down(int x,int l,int r){
    if(l==r) return ;
    int k=v[x]; v[x]=0;
    v[x<<1]^=k; v[x<<1^1]^=k;
}
void push_add(int x,int l,int r){
    if(L<=l&&r<=R){
        v[x]^=W;
        return ;
    }
    if(l==r) return ;
    if(v[x]) down(x,l,r);
    int mid=(l+r)>>1;;
    if(L<=mid) push_add(x<<1,l,mid);
    if(R>mid) push_add(x<<1^1,mid+1,r);
}
int find(int x,int l,int r,int k){
    if(l==r) return v[x];
    int mid=(l+r)>>1;
    if(v[x]) down(x,l,r);
    if(k<=mid) return find(x<<1,l,mid,k);
    else return find(x<<1^1,mid+1,r,k);
}
int main()
{
    int x,y,w;
    T=read();
    while(T--){
        prepare();
        n=read();
        for(int i=1;i<n;i++){
            x=read(),y=read(),w=read(),insert(x,y,w);
            cnt2++; e1[cnt2].to=y; e1[cnt2].from=x; e1[cnt2].w=w;
        }
        dfs(1); //for(int i=1;i<=n;i++) printf("[%d %d]\n",l[i],r[i]);
        top=cnt2; sort(e1+1,e1+1+cnt2,cmp1); //printf("[%d]\n",cnt);
        m=read(); for(int i=1;i<=m;i++) q[i].u=read(),q[i].v=read(),q[i].w=read(),q[i].pos=i;
        sort(q+1,q+1+m,cmp2); //for(int i=1;i<=m;i++) printf("[%d %d %d]\n",q[i].u,q[i].v,q[i].w);
        //for(int i=1;i<=cnt;i++) printf("[%d %d %d]\n",e[i].from,e[i].to,e[i].w);
        for(int i=1;i<=m;i++){
            int mx=q[i].w;
            while(top&&e1[top].w<=mx){
                x=e1[top].from; y=e1[top].to; w=e1[top].w; //printf("[%d %d %d] %d\n",x,y,w,top);
                top--;//printf("%d\n",top);
                if(deep[x]<deep[y]) swap(x,y);
                L=l[x]; R=r[x]; W=w; //printf("%d %d %d\n",L,R,W);
                push_add(1,1,n);
            }//printf("~~~\n");
            int sum1=find(1,1,n,l[q[i].u]),sum2=find(1,1,n,l[q[i].v]);
            ans[q[i].pos]=sum1^sum2;
        }
        for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    }
    return 0;
}

时间: 2024-10-05 15:57:40

codechef T6 Pishty and tree dfs序+线段树的相关文章

POJ3321 - Apple Tree DFS序 + 线段树或树状数组

Apple Tree:http://poj.org/problem?id=3321 题意: 告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这个点上的苹果,就是有就去掉,没有就加上. 思路: 先对树求一遍dfs序,每个点保存一个l,r.l是最早到这个点的时间戳,r是这个点子树中的最大时间戳,这样就转化为区间问题,可以用树状数组,或线段树维护区间的和. #include <algorithm> #include <iterator&

codeforces 617E. New Year Tree dfs序+线段树+bitset

题目链接 给一棵树, 每个节点有颜色, 两种操作, 一种是将一个节点的子树全都染色成c, 一种是查询一个节点的子树有多少个不同的颜色, c<=60. 每个节点一个bitset维护就可以. #include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]

Educational Codeforces Round 6 E dfs序+线段树

题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili上电子科技大学发的视频学习的 将一颗树通过dfs编号的方式 使每个点的子树的编号连在一起作为相连的区间 就可以配合线段树搞子树 因为以前好像听说过 线段树可以解决一种区间修改和查询区间中不同的xx个数...所以一下子就想到了... 但是我不会写线段树..只会最简单的单点修改区间查询...不会用延迟标

【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线)

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

codevs1228 (dfs序+线段树)

总结: 第一次遇到dfs序的问题,对于一颗树,记录节点 i 开始搜索的序号 Left[i] 和结束搜索的序号 Righti[i],那么序号在 Left[i] ~ Right[i] 之间的都是节点 i 子树上的节点. 并且此序号与线段树中 L~R 区间对应,在纸上模拟了几遍确实如此,但暂时还未理解为何对应. 此题就是dfs序+线段树的裸题 代码: #include<iostream> #include<vector> #include<cstring> #include&

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网