[SCOI2015]情报传递

[SCOI2015]情报传递

BZOJ
luogu
考虑什么样的点会对某个询问贡献答案,
设每个点的开始搜集情报时间为(t_i),那么每次询问就是要求链上有多少点i满足$$now-t_i>c$$
移项就有$$t_i<now-c$$
相当于求链上满足条件的点数,但是带修改依然不太好做
我们发现对于一个询问后面的修改操作的(t_i)一定大于now,所以后面修改的点一定不影响前面询问的答案,
想到什么?
可以离线询问,将所有修改操作处理完,最后直接主席树查就行了,复杂度(O(nlogn))

#include<bits/stdc++.h>
using namespace std;
const int _=2e5+5;
int re(){
	int x=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*w;
}
bool vis[_];
vector<int>p[_];
int n,o,q,cnt,tot;
int s[_*20],ls[_*20],rs[_*20];
int h[_],rt[_],t[_],a[_],b[_],lim[_],fa[_],f[_],lca[_],dep[_];
struct edge{int to,next;}e[_<<1];
void link(int u,int v){
	e[++cnt]=(edge){v,h[u]};h[u]=cnt;
	e[++cnt]=(edge){u,h[v]};h[v]=cnt;
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void upd(int&x,int l,int r,int k){
	s[++tot]=s[x]+1;ls[tot]=ls[x];rs[tot]=rs[x];
	x=tot;if(l==r)return;int mid=l+r>>1;
	k<=mid?upd(ls[x],l,mid,k):upd(rs[x],mid+1,r,k);
}
int query(int A,int B,int C,int D,int l,int r,int k){
	if(r<=k)return s[A]+s[B]-s[C]-s[D];
	int mid=l+r>>1,res=query(ls[A],ls[B],ls[C],ls[D],l,mid,k);
	if(k>mid)res+=query(rs[A],rs[B],rs[C],rs[D],mid+1,r,k);return res;
}
void dfs(int u){
	rt[u]=rt[fa[u]];
	if(t[u])upd(rt[u],1,q,t[u]);
	for(int i=h[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa[u])continue;
		dep[v]=dep[u]+1;dfs(v);f[v]=u;
	}
	vis[u]=1;
	for(int i=0,j=p[u].size();i<j;i++){
		int k=p[u][i],v=a[k]==u?b[k]:a[k];
		if(vis[v])lca[k]=find(v);
	}
}
int main(){
	n=re();
	for(int i=1;i<=n;i++){
		fa[i]=re();if(!fa[i])o=i;
		link(i,fa[i]);f[i]=i;
	}
	q=re();int op,x;
	for(int i=1;i<=q;i++){
		op=re();x=re();
		if(op==2&&!t[x])t[x]=i;
		if(op==1){
			a[i]=x,b[i]=re(),lim[i]=i-re()-1;
			p[x].push_back(i);p[b[i]].push_back(i);
		}
	}
	dep[1]=1;dfs(o);
	for(int i=1;i<=q;i++)
		if(a[i])printf("%d %d
",dep[a[i]]+dep[b[i]]-dep[lca[i]]-dep[fa[lca[i]]],lim[i]>=1?query(rt[a[i]],rt[b[i]],rt[lca[i]],rt[fa[lca[i]]],1,q,lim[i]):0);
	return 0;
}
原文地址:https://www.cnblogs.com/sdzwyq/p/9928812.html