4129: Haruna’s Breakfast

第二道树上莫队。。

这个的话要对数值进行分块才可以跑

其实也差不是太多

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=100005;
int n,m,q;
int block;
int val[N],w[N];
int c[N],pre[N];
struct ask{int l,r,t,lb,rb,id;}a[N];
struct UP{int x,y,pre;}b[N];
int cnt2=0,cnt1=0;
struct qq{int x,y,last;}s[N*2];int num,last[N];
void init (int x,int y)
{
	num++;
	s[num].x=x;s[num].y=y;
	s[num].last=last[x];
	last[x]=num;
}
int dep[N],cc=0;
int f[N][20];
int sta[N],top=0;
int belong[N],ecnt=0;
bool vis[N];//是否在莫队里面 
int dfs (int x,int fa)
{
	int remain=0;
	for (int u=last[x];u!=-1;u=s[u].last)
	{
		int y=s[u].y;
		if (y==fa) continue;
		f[y][0]=x;dep[y]=dep[x]+1;
		remain+=dfs(y,x);
		if (remain>=block)
		{
			ecnt++;
			while (remain>0) belong[sta[top--]]=ecnt,remain--;
		}
	}
	sta[++top]=x;
	return remain+1;
}
bool cmp(ask q,ask qq)
{
    if(q.lb==qq.lb && q.rb==qq.rb) return q.t<qq.t;
    if(q.lb==qq.lb) return q.rb<qq.rb;
    return q.lb<qq.lb;
}
int AA[N],BB[N],Belong[N],L[N],shen;//这个块有多少个元素    这个数字出现了多少次   这个数字时哪一个块的   这一个块的左边界
void prepare ()//分块 
{
	shen=1;L[1]=0;AA[shen]=0;
	int cnt=0;
	for (int u=0;u<=n;u++)
	{
		cnt++;
		BB[u]=0;
		Belong[u]=shen;
		if (cnt==block)
		{
			cnt=0;
			shen++;
			AA[shen]=0;
			L[shen]=u+1;
		}
	}
}
void update (int x)//改变这个点的状态 
{
	if (c[x]>n) return ;
	if (vis[x])//这个在莫队里面
	{
		//printf("-%d %d\n",c[x],BB[c[x]]);
		vis[x]=false;
		BB[c[x]]--;
		if (BB[c[x]]==0) AA[belong[c[x]]]--;
	}
	else
	{
		//printf("+%d %d\n",c[x],BB[c[x]]);
		vis[x]=true;
		BB[c[x]]++;
		if (BB[c[x]]==1) AA[belong[c[x]]]++;
	}
/*	for (int u=0;u<=shen;u++) printf("%d ",AA[u]);
	printf("\n");*/
}
void lalal (int x,int C)//将x修改为C
{
	if (!vis[x]) c[x]=C;
	else
	{
		update(x);
		c[x]=C;
		update(x);
	}
}
void change (int x,int y)//加上这个路径   但是LCA还不在 
{
	while (x!=y)
	{
		if (dep[x]<dep[y]) update(y),y=f[y][0];
		else update(x),x=f[x][0];
	}
}
int A[N];
int lca (int x,int y)
{
	if (dep[x]>dep[y]) swap(x,y);
	for (int u=18;u>=0;u--) if (dep[f[y][u]]>=dep[x]) y=f[y][u];
	if (x==y) return x;
	for (int u=18;u>=0;u--)
		if (f[y][u]!=f[x][u])
			y=f[y][u],x=f[x][u];	
	return f[x][0];
}
int Get ()
{
	int x=1;
	while (AA[x]==block)	x++;
	x=L[x];
	while (BB[x]!=0) x++;
	return x;
}
void solve ()
{
	sort(a+1,a+1+cnt1,cmp);cnt2=a[1].t;
	memset(vis,false,sizeof(vis));
	for (int u=1;u<=a[1].t;u++) lalal(b[u].x,b[u].y);
	change(a[1].l,a[1].r);
	//printf("ans:%d\n",Get());
	int LCA=lca(a[1].l,a[1].r);update(LCA);//可以知道,我们这条链还少了LCA
	A[a[1].id]=Get();
	update(LCA);//这里的目的是为了下一次的跳,可以知道,当我们更改的是后,l,r的路径要是空,否则会有多算
	for (int u=2;u<=cnt1;u++)
	{
		while (cnt2<a[u].t) cnt2++,lalal(b[cnt2].x,b[cnt2].y);
		while (cnt2>a[u].t) lalal(b[cnt2].x,b[cnt2].pre),cnt2--;
		change(a[u].l,a[u-1].l);change(a[u].r,a[u-1].r);
		LCA=lca(a[u].l,a[u].r);
		update(LCA);
		A[a[u].id]=Get();
	//	printf("ans:%d\n",Get());
		update(LCA);
	}
}
void ins ()
{
	num=0;memset(last,-1,sizeof(last));
	scanf("%d%d",&n,&q);
	block=(int)pow(n,0.60);
	for (int u=1;u<=n;u++) {scanf("%d",&c[u]);pre[u]=c[u];}
	for (int u=1;u<n;u++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		init(x,y);init(y,x);
	}
	ecnt=0;dep[1]=1;dfs(1,0);
	for (int u=1;u<=17;u++)
		for (int i=1;i<=n;i++)
			f[i][u]=f[f[i][u-1]][u-1];
	for (int u=1;u<=q;u++)
	{
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if (op==0) {b[++cnt2].pre=pre[x];b[cnt2].x=x;b[cnt2].y=y;pre[x]=y;}
		else
		{
			a[++cnt1].l=x;a[cnt1].r=y;
			a[cnt1].lb=belong[x];a[cnt1].rb=belong[y];
			a[cnt1].t=cnt2;a[cnt1].id=cnt1;
		}
	}
}
int main()
{
	ins();
	prepare();
	//for (int u=0;u<=n;u++) printf("YES:%d %d\n",Belong[u],L[Belong[u]]);
	solve();
	for (int u=1;u<=cnt1;u++) printf("%lld\n",A[u]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值