[ZJOI2007]捉迷藏 (线段树,括号序列)
大意: 给定树, 要求维护一个点集, 支持删点添点, 询问点集直径.
本题做法比较多.
一个显然的做法是, 线段树维护区间直径, 然后根据点集直径的性质, 合并后直径端点一定是四个端点其中两个, 枚举取最大即可.
如果用树剖求$lca$, 复杂度就为$O(nlog^2n)$.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
using namespace std;
const int N = 1e5+10;
int n, m, f[N];
int dep[N], sz[N], top[N], fa[N], son[N];
vector<int> g[N];
void dfs(int x, int f, int d) {
fa[x]=f,dep[x]=d,sz[x]=1;
for (int y:g[x]) if (y!=f) {
dfs(y,x,d+1),sz[x]+=sz[y];
if (sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs(int x, int tf) {
top[x]=tf;
if (son[x]) dfs(son[x],tf);
for (int y:g[x]) if (!top[y]) dfs(y,y);
}
int lca(int x, int y) {
while (top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int dis(int x, int y) {
if (!x||!y) return 0;
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
struct _ {
int A,B,d;
_ (int A=0,int B=0,int d=-1) :A(A),B(B),d(d) {}
bool operator < (const _ &rhs) const {
if (d!=rhs.d) return d<rhs.d;
return !!A+!!B<!!rhs.A+!!rhs.B;
}
_ operator + (const _ &rhs) const {
int c[4]={A,B,rhs.A,rhs.B};
_ t;
REP(i,0,3) REP(j,i+1,3) {
_ tt(c[i],c[j],dis(c[i],c[j]));
if (t<tt) t = tt;
}
return t;
}
} tr[N<<2];
void update(int o, int l, int r, int x) {
if (l==r) tr[o] = (f[l]^=1)?_(l):_();
else mid>=x?update(ls,x):update(rs,x),tr[o]=tr[lc]+tr[rc];
}
void build(int o, int l, int r) {
if (l==r) f[l] = 1, tr[o] = _(l);
else build(ls),build(rs),tr[o]=tr[lc]+tr[rc];
}
int main() {
scanf("%d", &n);
REP(i,2,n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].pb(v),g[v].pb(u);
}
dfs(1,0,0),dfs(1,1);
build(1,1,n);
int m;
scanf("%d", &m);
while (m--) {
char op;
int x;
scanf(" %c", &op);
if (op=='G') printf("%d\n", tr[1].d);
else scanf("%d", &x),update(1,1,n,x);
}
}
还有一种做法是利用括号序列.

先序遍历后写成:$[A[B[E][F[H][I]]][C][D[G]]]$
考虑节点$E$和$G$, 取出括号编码 $][[][]]][][[$
删掉匹配的括号得到 $]][[$
意味着从$E$往上两步再往下两步就可以到达$G$.
所以树上一条路径可以表示为一段括号序列$S$, 然后$S$可以用一个二元组$S(a,b)$表示.
那么这个题需要动态维护$dis(S)=\{a+b|S'(a,b)$为$S$的子串, 且介于两黑点间$\}$.
对于括号序列$S$, 维护$7$个量$l,r,dis,L\_plus,L\_minus,R\_plus,R\_minus$
$l,r$为$S$的二元组, $dis$为黑点最大间距.
$L\_plus$为 $max\{l+r|S'$是$S$的前缀,且$S'$后为黑点$\}$.
$L\_minus$为 $max\{r-l|S'$是$S$的前缀,且$S'$后为黑点$\}$.
$R\_plus$为 $max\{l+r|S'$是$S$的后缀,且$S'$前为黑点$\}$.
$R\_minus$为 $max\{l-r|S'$是$S$的后缀,且$S'$前为黑点$\}$.
实现时把字母也添进括号序列, 用线段树维护每个量即可.
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
using namespace std;
const int N = 3e5+10, INF = 0x3f3f3f3f;
int n, m, sum, no[N], a[N], vis[N];
vector<int> g[N];
void dfs(int x, int f) {
a[++*a] = -1, a[++*a] = x;
for (int y:g[x]) if (y!=f) {
dfs(y,x);
}
a[++*a] = -2;
}
struct _ {
int l,r,dis,l_plus,l_minus,r_plus,r_minus;
_ (int l=0,int r=0,int dis=0,int l_plus=0,int l_minus=0,int r_plus=0,int r_minus=0) : l(l),r(r),dis(dis),l_plus(l_plus),l_minus(l_minus),r_plus(r_plus),r_minus(r_minus) {}
_ operator + (const _ &rhs) const {
_ ret;
ret.l = l+max(rhs.l-r,0);
ret.r = rhs.r+max(r-rhs.l,0);
ret.l_plus = max({l_plus,l+r+rhs.l_minus,l-r+rhs.l_plus});
ret.l_minus = max(l_minus,rhs.l_minus+r-l);
ret.r_plus = max({rhs.r_plus,r_plus-rhs.l+rhs.r,r_minus+rhs.l+rhs.r});
ret.r_minus = max(rhs.r_minus,r_minus+rhs.l-rhs.r);
ret.dis = max({dis,rhs.dis,r_plus+rhs.l_minus,r_minus+rhs.l_plus});
return ret;
}
} tr[N<<2]; void build(int o, int l, int r) {
if (l==r) {
if (a[l]>0) no[a[l]]=l,tr[o]=_(0,0,-INF);
else tr[o]=_(a[l]==-2,a[l]==-1,-INF,-INF,-INF,-INF,-INF);
}
else build(ls),build(rs),tr[o]=tr[lc]+tr[rc];
}
void update(int o, int l, int r, int x) {
if (l==r) {
if (vis[l]) vis[l]=0,tr[o]=_(0,0,-INF);
else vis[l]=1,tr[o]=_(0,0,-INF,-INF,-INF,-INF,-INF);
}
else mid>=x?update(ls,x):update(rs,x),tr[o]=tr[lc]+tr[rc];
}
int main() {
scanf("%d", &n),sum=n;
REP(i,2,n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].pb(v),g[v].pb(u);
}
dfs(1,0);
build(1,1,*a);
scanf("%d", &m);
while (m--) {
char op;
int x;
scanf(" %c", &op);
if (op=='G') {
int ans = tr[1].dis;
if (sum==0) ans = -1;
if (sum==1) ans = 0;
printf("%d\n", ans);
}
else scanf("%d",&x),update(1,1,*a,no[x]);
}
}
[ZJOI2007]捉迷藏 (线段树,括号序列)的更多相关文章
- BZOJ.1095.[ZJOI2007]捉迷藏(线段树 括号序列)
BZOJ 洛谷 对树DFS得到括号序列.比如这样一个括号序列:[A[B[E][F[H][I]]][C][D[G]]]. 那比如\(D,E\)间的最短距离,就是将\(D,E\)间的括号序列取出:][[] ...
- [bzoj1095][ZJOI2007]Hide 捉迷藏——线段树+括号序列
题目大意 给定一棵所有点初始值为黑的无权树,你需要支援两种操作: 把一个点的颜色反转 统计最远黑色点对. 题解 本题是一个树上的结构.对于树上的结构,我们可以采用点分治.树链剖分等方法处理,这个题用了 ...
- [BZOJ 1095] [ZJOI2007]Hide 捉迷藏——线段树+括号序列(强..)
神做法-%dalao,写的超详细 konjac的博客. 如果觉得上面链接的代码不够优秀好看,欢迎回来看本蒟蒻代码- CODE WITH ANNOTATION 代码中−6-6−6表示左括号'[',用−9 ...
- Tree Generator™ CodeForces - 1149C (线段树,括号序列)
大意: 给定括号序列, 每次询问交换两个括号, 求括号树的直径. 用[ZJOI2007]捉迷藏的方法维护即可. #include <iostream> #include <algor ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治
这题真是十分难写啊 不管是点分治还是括号序列都有一堆细节.. 点分治:时空复杂度$O(n\log^2n)$,常数巨大 主要就是3个堆的初始状态 C堆:每个节点一个,为子树中的点到它父亲的距离的堆. B ...
- BZOJ 1095: [ZJOI2007]Hide 捉迷藏(线段树维护括号序列)
这个嘛= =链剖貌似可行,不过好像代码长度很长,懒得打(其实是自己太弱了QAQ)百度了一下才知道有一种高大上的叫括号序列的东西= = 岛娘真是太厉害了,先丢链接:http://www.shuizilo ...
- BZOJ 1095 捉迷藏(线段树维护括号序列)
对于树的一个括号序列,树上两点的距离就是在括号序列中两点之间的括号匹配完之后的括号数... 由此可以得出线段树的做法.. #include<cstdio> #include<iost ...
- poj2886(线段树求序列第k小)
题目链接:https://vjudge.net/problem/POJ-2886 题意:n个人围成一个圈,每个人有姓名s和权值val两个属性,第一轮序号为k的人退出,并根据其val指定下一个人,val ...
- poj2828(线段树查找序列第k小的值)
题目链接:https://vjudge.net/problem/POJ-2828 题意:有n个人,依次给出这n个人进入队列时前面有多少人p[i],和它的权值v[i],求最终队列的权值序列. 思路:基本 ...
随机推荐
- Python3中转换字符串编码
在使用subprocess调用Windows命令时,遇到了字符串不显示中文的问题,源码如下:#-*-coding:utf-8-*-__author__ = '$USER' #-*-coding:utf ...
- Linux中man命令的使用方法再解释
原文链接:http://www.linuxidc.com/Linux/2017-03/142407.htm Linux提供了丰富的帮助手册,当你需要查看某个命令的参数时不必到处上网查找,只要man一下 ...
- 关于Flutter启动项目白屏,报错[ERROR:flutter/shell/gpu/gpu_surface_gl.cc(58)] Failed to setup Skia Gr context.问题的解决方案
首先,环境如下: 1.系统:windows10 64位 Android SDK version: 28.0.3 Flutter SDK: v1.5.4-hotfix.2 模拟器: 网易Mu ...
- 判断 js 类型的方式
1. typeof 可以判断出'string','number','boolean','undefined','symbol'但判断 typeof(null) 时值为 'object'; 判断数组和对 ...
- idea备忘
1.idea 最近打开的文件个数 File->Settings->Editor->General->Editor Tabs->Tab Closing Policy-> ...
- Eclipse自动生成作者、日期注释等功能设置 (转载)
原文地址:http://blog.sina.com.cn/s/blog_4080505a0101guoh.html 在使用Eclipse 编写Java代码时,自动生成的注释信息都是按照预先设置好的格式 ...
- latex运算符
一些小的运算符,可以在数学模式下直接输入,但是有一些运算符需要用控制序列生成:
- VBScript把json字符串解析成json对象的2个方法
这篇文章主要介绍了VBScript把json字符串解析成json对象的2个方法,本文通过MSScriptControl.ScriptControl和jscript实现,需要的朋友可以参考下 asp/v ...
- sbt配置文件
# Set the java args to high -Xmx512M -XX:MaxPermSize=256m -XX:ReservedCodeCacheSize=128m # Set the e ...
- react中,用key值来解决一些奇葩问题
编辑用户信息,角色信息无法加载到值 改进之后:思路:由于值是设置在state里面的,界面编辑时,会重服务器拉去数据,值也设置在state里面了,但是CheckboxGroup依然不会去渲染选中的值, ...