CF342E Xenia and Tree

题面传送门
看完了题目你会发现这道题居然没有撤销操作。
那么基本上就是某些奇奇怪怪的算法
首先我们考虑暴力
第一种想法是暴力对于每个红点求 l c a lca lca
第二种想法是对于每个新加进来的红点做一遍 b f s bfs bfs然后每次查询直接 O ( 1 ) O(1) O(1)
一般这种有两个暴力然后分别两个操作复杂度大大低于另外一个的就是根号分治。
考虑对操作分块,设块长为 K K K
那么对于每一块的查询,对这一块中的修改取lca然后再和之前 b f s bfs bfs的取 m i n min min,每一块结束后进行 b f s bfs bfs,注意这样 b f s bfs bfs O ( n ) O(n) O(n)的。
然后复杂度就是 O ( n 2 K + n l o g n K ) O(\frac{n^2}{K}+nlognK) O(Kn2+nlognK)
这样子就是 K = n l o g n K=\sqrt{\frac{n}{logn}} K=lognn 时取到最优 O ( n n l o g n ) O(n\sqrt{nlogn}) O(nnlogn )
但是这样子还不是最快的。
其实那个块内询问有 1 4 \frac{1}{4} 41的小常数,读者自证不难,所以实际块长取 O ( 1 2 n n l o g n ) O(\frac{1}{2}n\sqrt{nlogn}) O(21nnlogn )
代码实现:

#include<cstdio>
#include<cmath>
#include<queue>
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,w[100039],ans,a[100039],ah,lcas,now;
int xs[100039],ys[100039];
int top[100039],d[100039],siz[100039],son[100039],fa[100039];
struct yyy{int to,z;}tmp;
struct ljb{
	int head,h[100039];
	yyy f[200039];
	inline void add(int x,int y){
		f[++head]=(yyy){y,h[x]};
		h[x]=head;
	}
}s;
inline void dfs1(int x,int last){
	fa[x]=last;d[x]=d[last]+1;siz[x]=1;yyy tmp;
	for(beg(x);end;go){
		tmp=s.f[cur];
		if(tmp.to!=last){
			dfs1(tmp.to,x);
			siz[x]+=siz[tmp.to];
			if(siz[son[x]]<siz[tmp.to]) son[x]=tmp.to;
		}
	}
}
inline void dfs2(int x,int last){
	top[x]=last;yyy tmp;
	if(!son[x]) return;
	dfs2(son[x],last);
	for(beg(x);end;go){
		tmp=s.f[cur];
		if(tmp.to!=fa[x]&&tmp.to!=son[x]) dfs2(tmp.to,tmp.to);
	}
}
inline void swap(int &x,int &y){x^=y^=x^=y;}
inline int lca(int x,int y){
	while(top[x]!=top[y]){
		if(d[top[x]]<d[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return d[x]<d[y]?x:y;
}
queue<int> q;
int main(){
	freopen("1.in","r",stdin);
	register int i,j,h;
	scanf("%d%d",&n,&m);k=max(sqrt(m*1.0/log2(m)),1);
	for(i=1;i<n;i++) scanf("%d%d",&x,&y),s.add(x,y),s.add(y,x);
	for(i=0;i<m;i++) scanf("%d%d",&xs[i],&ys[i]);
	dfs1(1,0);dfs2(1,1);
	for(i=1;i<=n;i++) w[i]=d[i]-1;
	for(i=0;i<=m/k;i++){
		ah=0;
		for(j=i*k;j<m&&j<=i*k+k-1;j++){
			if(xs[j]==2){
				ans=w[ys[j]];
				for(h=1;h<=ah;h++) lcas=lca(ys[j],a[h]),ans=min(ans,d[ys[j]]+d[a[h]]-d[lcas]*2);
				printf("%d\n",ans);
			}
			else a[++ah]=ys[j];
		}
		for(j=1;j<=ah;j++)q.push(a[j]),w[a[j]]=0;
		while(!q.empty()){
			now=q.front();q.pop();
			for(beg(now);end;go){
				tmp=s.f[cur];
				if(w[tmp.to]>w[now]+1)w[tmp.to]=w[now]+1,q.push(tmp.to);  
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值