2019.03.21【ZJOI2007】【BZOJ1095】【洛谷P2056】Hide 捉迷藏(DFS序)(线段树)

BZOJ传送门

洛谷传送门


解析:

其实就是QTREE4的弱化版,可以直接用QTREE4的解法来做。

但是这道题有优秀的 O ( n log ⁡ n ) O(n\log n) O(nlogn)做法。

我们考虑利用DFS出的括号序列来处理距离。

将点的编号塞到括号序列里面,那么两个点的距离就是它们中间的括号序列的非匹配括号数。

证明因为极度弱智不想写了。

现在考虑用线段树来维护这个括号序列。

考虑如何得到横跨两个区间分界点的所有白色点对的距离最大值。

考虑区间 l l l的某个后缀 ( a 1 , b 1 ) (a1,b1) (a1,b1),表示这个区间有 a 1 a1 a1个无法匹配的右括号和 b 1 b1 b1个无法匹配的左括号

同理考虑区间 r r r的某个前缀 ( a 2 , b 2 ) (a2,b2) (a2,b2)

显然这样两个前后缀拼起来表示的距离就是 a 1 + a b s ( b 1 − a 2 ) + b 2 a1+abs(b1-a2)+b2 a1+abs(b1a2)+b2,显然我们需要的就是 max ⁡ ( a 1 + a b s ( b 1 − a 2 ) + b 2 ) \max(a1+abs(b1-a2)+b2) max(a1+abs(b1a2)+b2)把里面的式子稍微转化一下得到: a 1 + max ⁡ ( a 2 − b 1 , b 1 − a 2 ) + b 2 = max ⁡ ( a 1 − b 1 + a 2 + b 2 , a 1 + b 1 − a 2 + b 2 ) a1+\max(a2-b1,b1-a2)+b2=\max(a1-b1+a2+b2,a1+b1-a2+b2) a1+max(a2b1,b1a2)+b2=max(a1b1+a2+b2,a1+b1a2+b2)

所以我们需要维护前缀的 − a + b , a + b -a+b,a+b a+ba+b的最大值,后缀 a − b , a + b a-b,a+b ab,a+b的最大值。

线段树上随便做啊。

对于黑点白点只需要在线段树叶子节点处理一下这四个值就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline char getalpha(){
		re char c;
		while(!isalpha(c=gc()));return c;
	}
}
using namespace IO;

using std::cout;
cs int N=1e5+5;
int n,q;
int last[N],to[N<<1],nxt[N<<1],ecnt;
inline void addedge(int u,int v){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}

int in[N*3],tot,pos[N];
void dfs(int u,int fa){
	in[++tot]=-1;
	in[++tot]=u;
	pos[u]=tot;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=fa)dfs(v,u);
	in[++tot]=-2;
}

cs int M=N*3<<2;
int a[M],b[M],l1[M],l2[M],r1[M],r2[M],dis[M];
bool col[N];

inline void init(int k,int id){
	dis[k]=-1;
	a[k]=b[k]=0;
	l1[k]=l2[k]=r1[k]=r2[k]=-1e9;
	switch(in[id]){
		case -1:b[k]=1;break;
		case -2:a[k]=1;break;
		default:if(!col[in[id]])l1[k]=l2[k]=r1[k]=r2[k]=dis[k]=0;break;
	}
}

inline void pushup(int k){
	int lc=k<<1,rc=k<<1|1;
	if(b[lc]>a[rc])a[k]=a[lc],b[k]=b[lc]-a[rc]+b[rc];
	else b[k]=b[rc],a[k]=a[lc]+a[rc]-b[lc];
	l1[k]=std::max(l1[lc],std::max(l1[rc]+a[lc]-b[lc],l2[rc]+a[lc]+b[lc]));
	l2[k]=std::max(l2[lc],l2[rc]-a[lc]+b[lc]);
	r1[k]=std::max(r1[rc],std::max(r1[lc]-a[rc]+b[rc],r2[lc]+a[rc]+b[rc]));
	r2[k]=std::max(r2[rc],r2[lc]+a[rc]-b[rc]);
	dis[k]=std::max(std::max(dis[lc],dis[rc]),std::max(r1[lc]+l2[rc],r2[lc]+l1[rc]));
}

inline void build(int k,int l,int r){
	if(l==r)return init(k,l);
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

inline void modify(int k,int l,int r,cs int &pos){
	if(l==r){
		col[in[pos]]^=1;
		init(k,pos);
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)modify(k<<1,l,mid,pos);
	else modify(k<<1|1,mid+1,r,pos);
	pushup(k);
}

signed main(){
	n=getint();
	for(int re i=1;i<n;++i)addedge(getint(),getint());
	dfs(1,0);
	build(1,1,tot);
	q=getint();
	while(q--)switch(getalpha()){
		case 'G':cout<<dis[1]<<"\n";break;
		case 'C':modify(1,1,tot,pos[getint()]);break;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值