## 题目描述

给出N个点的一棵树(N-1条边),节点有白有黑,初始全为白

有两种操作:

0 i : 改变某点的颜色(原来是黑的变白,原来是白的变黑)

1 v : 询问1到v的路径上的第一个黑点,若无,输出-1

输入输出格式

输入格式:

第一行 N,Q,表示N个点和Q个操作

第二行到第N行N-1条无向边

再之后Q行,每行一个操作"0 i" 或者"1 v" (1 ≤ i, v ≤ N).

输出格式:

对每个1 v操作输出结果

输入输出样例

输入样例#1: 复制

9 8

1 2

1 3

2 4

2 9

5 9

7 9

8 9

6 8

1 3

0 8

1 6

1 7

0 2

1 9

0 2

1 9

输出样例#1: 复制

-1

8

-1

2

-1

说明

For 1/3 of the test cases, N=5000, Q=400000.

For 1/3 of the test cases, N=10000, Q=300000.

For 1/3 of the test cases, N=100000, Q=100000.

Solution

这题显然是树链剖分,先把dfs序处理出来,但线段树存什么呢?可以发现,若以1为根,进行dfs序,这样1到每个点之间的最早出现的黑点,显然是dfs序最小值,这样线段树存最小值就行了,我们还要把线段树上的点映射回树中结点。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll(x) (x*2)
#define rr(x) (x*2+1)
using namespace std;
struct Node
{
int to,next;
}a[201001];
int len,last[200110],siz[201010],son[201100],dep[201001],fa[200010],yuan[200101];
int id[201010],cnt,n,sum[400101],top[400101],mn[400110];
void add(int a1,int a2)
{
a[++len].to=a2;
a[len].next=last[a1];
last[a1]=len;
}
void dfs1(int x,int father)
{
int mxson=-1;siz[x]=1;
for(int i=last[x];i;i=a[i].next)
{
int to=a[i].to;
if(to==father) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to,x);
siz[x]+=siz[to];
if(siz[to]>mxson) son[x]=to,mxson=siz[to];
}
}
void dfs2(int x,int topf)
{
id[x]=++cnt;
yuan[cnt]=x;
top[x]=topf;
if(!son[x]) return;
dfs2(son[x],topf);
for(int i=last[x];i;i=a[i].next)
{
int to=a[i].to;
if(to==fa[x]||to==son[x]) continue;
dfs2(to,to);
}
}
void pushup(int node){mn[node]=min(mn[ll(node)],mn[rr(node)]);}
void gai(int node,int l,int r,int pos)
{
if(l==r) {sum[node]^=1;if(sum[node]==1) mn[node]=l;else mn[node]=1e9;return;}
int mid=(l+r)/2;
if(pos<=mid) gai(ll(node),l,mid,pos);
else gai(rr(node),mid+1,r,pos);
pushup(node);
}
int cha(int node,int l,int r,int left,int right)
{
if(l>=left&&r<=right) return mn[node];
int mid=(l+r)/2,ans=1e9;
if(left<=mid) ans=min(ans,cha(ll(node),l,mid,left,right));
if(right>mid) ans=min(ans,cha(rr(node),mid+1,r,left,right));
return ans;
}
int cha1(int x,int y)
{
int ans=1e9;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=min(ans,cha(1,1,n,id[top[x]],id[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans=min(ans,cha(1,1,n,id[x],id[y]));
if(ans==1e9)
return -1;
return yuan[ans];
}
int main()
{
for(int i=0;i<=400000;i++)
mn[i]=1e9;
int q,x,y,opt;
cin>>n>>q;
for(int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
dep[1]=1;
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&opt,&x);
if(opt==0)
gai(1,1,n,id[x]);
else
printf("%d\n",cha1(1,x));
}
}

博主蒟蒻,可以随意转载,但必须附上原文链接k-z-j

Qtree3的更多相关文章

  1. [题解]luogu P4116 Qtree3

    终于来到了Qtree3, 其实这是Qtree系列中最简单的一道题,并不需要线段树, 只要树链剖分的一点思想就吼了. 对于树链剖分剖出来的每一根重链,在重链上维护一个Set就好了, 每一个Set里存的都 ...

  2. SPOJ QTREE3 Query on a tree again! ——Link-Cut Tree

    [题目分析] QTREE2,一看是倍增算法,太懒了,不写了.( ̄_, ̄ ) QTREE3,树链剖分可以做,发现链上的问题LCT也很好做. 要是子树问题貌似可以DFS序. 然后就成LCT模板题了. 考前 ...

  3. 树链剖分&咕咕咕了好久好久的qtree3

    前言 显然qtree系列都是树链剖分辣 发现自己没有专门整理过树链剖分耶 辣么就把这篇博客魔改成树链剖分好辣(貌似除了树剖也没什么好写的) 正文 废话了辣么多终于开始了 一.树剖怎么写鸭 二.树剖有什 ...

  4. QTREE3 spoj 2798. Query on a tree again! 树链剖分+线段树

    Query on a tree again! 给出一棵树,树节点的颜色初始时为白色,有两种操作: 0.把节点x的颜色置反(黑变白,白变黑). 1.询问节点1到节点x的路径上第一个黑色节点的编号. 分析 ...

  5. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...

  6. P4116 Qtree3

    思路 可以树剖可以LCT,树剖就是每个重链开一个SET维护一下黑点的深度 非常不优美 使用LCT,在splay上二分找出需要的节点即可 代码 #include <cstdio> #incl ...

  7. Qtree3题解(树链剖分+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意 很易懂吧.. 题解 我的做法十分的暴力:树链剖分(伪)+线段树+ std :: set ...

  8. 洛谷 P4116 Qtree3

    Qtree系列第三题 我是题面 读完题大概不难判断是一道树剖的题 这道题的关键是记录两种状态,以及黑点的序号(不是编号) 线段树啊当然 定义两个变量v,f,v表示距离根节点最近的黑点,默认-1,f则表 ...

  9. 【洛谷 P4116】 Qtree3 (树链剖分)

    题目链接 树剖练手题,想复习下树剖. 第一次提交\(T\)成QQC 看我 ??? 看了数据范围的确挺恶心的,我的复杂度是\(O(Mlog^2N)\)的,数据范围有三段 For 1/3 of the t ...

  10. 树链剖分【p4116】Qtree3 - Query on a tree

    Description 给出N个点的一棵树(N-1条边),节点有白有黑,初始全为白 有两种操作: 0 i : 改变某点的颜色(原来是黑的变白,原来是白的变黑) 1 v : 询问1到v的路径上的第一个黑 ...

随机推荐

  1. 标准C程序设计七---104

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  2. 16深入理解C指针之---迷途指针

    一.若程序中存在迷途指针,轻则导致程序退出,重则使程序出现重大逻辑错误 1.定义:内存已释放,指针依旧指向原始内存,这种指针就是迷途指针 2.迷途指针和指针别名: 1).指针依旧指向已释放的内存,无法 ...

  3. php 几种排序模式

    冒泡排序 冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工>作 ...

  4. POJ 3468:A Simple Problem with Integers(线段树[成段更新])

    题意:N个数Q次操作.一共两种操作:Q l r :询问[l,r]这个区间里的数字和,C l r c: [l,r]区间里的每个数都加上c.1 ≤ N,Q ≤ 100000. 方法:线段树的成段更新.注意 ...

  5. (五)github删除仓库

    一.一直学习怎么创建仓库,创建了太多仓库,一直不知道咋删除,有点懵,其实很简单,就是对英文不太习惯,要加深英文水平. 找到setting,然后再下面找到danger Zone

  6. HDU 3534 Tree (经典树形dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3534 题意: 给你一棵树,问你有多少对点的距离等于树的直径. 思路: dp[i][0]表示在i的子树中 ...

  7. stun简介

    转载 http://blog.csdn.net/mazidao2008/article/details/4934257 STUN(Simple Traversal of UDP over NATs,N ...

  8. android -- 存储byte

    public static String byteArrayToHexStr(byte[] byteArray) { if (byteArray == null){ return null; } ch ...

  9. 快讯 | FireEye在GitHub上开源密码破解工具GoCrack

    近日,FireEye 开源了一款密码破解工具 GoCrack,可在多机器上部署破解任务. GoCrack 是由 FireEye’s Innovation and Custom Engineering ...

  10. libsvm交叉验证与网格搜索(参数选择)

    首先说交叉验证.交叉验证(Cross validation)是一种评估统计分析.机器学习算法对独立于训练数据的数据集的泛化能力(generalize), 能够避免过拟合问题.交叉验证一般要尽量满足:1 ...