bzoj 4448: [Scoi2015]情报传递 dfs序列&树状数组

       首先遍历得到dfs序列,离线后按照C排序再从小到大插入和查询,每一次相当于单点修改和链上查询,然后用树状数组来维护区间修改单点查询就好了。(根本不需要用什么树链剖分)。O(NlogN)+常数小,成功用小号刷到rank2。

       这道题目其实是可以在线的,C相当于一个颜色,然后把dfs序列改成括号序列(这样就不会有多次修改重复用一个东西了)。使用带修改的主席树(树状数组套可持久化线段树)或者权值线段树套平衡树即可。不过主席树很可能会MLE。

       另外,树剖求lca真心好用。(感觉比倍增块)

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;

int n,m,cnt,tot,dfsclk,fst[N],pnt[N],nxt[N],sz[N],fa[N],d[N],son[N],anc[N];
int pos[N][2],c[N]; struct node{ int x,y,z,id,k; }a[N],ans[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	int p; sz[x]=1;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x]){
			fa[y]=x; d[y]=d[x]+1;
			dfs(y); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void divide(int x,int tp){
	int p; pos[x][0]=pos[x][1]=++dfsclk; anc[x]=tp;
	if (son[x]){ divide(son[x],tp); pos[x][1]=pos[son[x]][1]; }
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=son[x] && y!=fa[x]){
			divide(y,y); pos[x][1]=pos[y][1];
		}
	}
}
void ins(int x,int t){
	for (; x<=n; x+=x&-x) c[x]+=t;
}
int getsum(int x){
	int t=0; for (; x; x-=x&-x) t+=c[x]; return t;
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return (d[x]<d[y])?x:y;
}
bool cmp(node u,node v){
	return u.z<v.z || u.z==v.z && u.k<v.k;
}
int main(){
	n=read(); int i,x;
	for (i=1; i<=n; i++){
		x=read(); if (x) add(x,i);
	}
	d[1]=1; dfs(1); divide(1,1);
	m=read();
	for (i=1; i<=m; i++){
		a[i].k=read();
		if (a[i].k==1){
			a[i].x=read(); a[i].y=read();
			a[i].z=i-read(); a[i].id=++cnt;
		} else{
			a[i].x=read(); a[i].z=i;
		}
	}
	sort(a+1,a+m+1,cmp);
	for (i=1; i<=m; i++)
		if (a[i].k==1){
			int u=lca(a[i].x,a[i].y),v=fa[u];
			ans[a[i].id].x=d[a[i].x]+d[a[i].y]-d[u]-d[v];
			ans[a[i].id].y=getsum(pos[a[i].x][0])+getsum(pos[a[i].y][0])-getsum(pos[u][0])-getsum(pos[v][0]);
		} else{
			ins(pos[a[i].x][0],1); ins(pos[a[i].x][1]+1,-1);
		}
	for (i=1; i<=cnt; i++) printf("%d %d\n",ans[i].x,ans[i].y);
	return 0;
}


by lych

2016.4.5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值