新手求助,题目Count on a tree II(一道树上莫队),报错RE
  • 板块学术版
  • 楼主Main_WF
  • 当前回复1
  • 已保存回复1
  • 发布时间2020/7/25 17:50
  • 上次更新2023/11/6 22:18:19
查看原帖
新手求助,题目Count on a tree II(一道树上莫队),报错RE
302750
Main_WF楼主2020/7/25 17:50
/*错误报告:在ICA时计算节点差值把 deep[x1]-deep[x2]写成了 x1-x2 
在计算二进制数位时 sum*=2写成了sum*2 
*/
#include<bits/stdc++.h>
using namespace std;

const int maxn=200100;

int n,m;//n个节点 m次询问
long long col[maxn]; 
int u[maxn];
int v[maxn];
int fir[maxn];
int next[maxn];
int deep[maxn];//表示节点深度 

struct node//查询结构体 
{
	int qu,qv,w;//第w次查询,因为后面要排序 
}q[maxn];
int ans[maxn];//记录每次查询答案 

int ol[2*maxn];//欧拉欧拉 
int cnt=0;//欧拉欧拉的长度 
int in[maxn];
int out[maxn];

int len;

int inl,inr;
int book[maxn];//记录当前区间内每种颜色的数量
int book2[maxn];
int alcol=0;//颜色总数量 

int fa[maxn][20];//fa[i][j]表示i节点的第2^j个父亲 

void read()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>col[i];
	for(int i=1;i<=n-1;i++)
	{
		cin>>u[i]>>v[i];
		fa[v[i]][0]=u[i];//数组初始化 
		next[i]=fir[u[i]];
		fir[u[i]]=i;
	}
	for(int i=1;i<=m;i++)//将查询储存起来 
		cin>>q[i].qu>>q[i].qv,q[i].w=i;
	len=sqrt(2*n);//一般块的大小啦 
}

long long log2(int x)//求x的二进制 数位-1
{
	long long sum=1;
	long long hg=0;
	while(sum<=x)
		sum*=2,hg++;
	return hg-1;
}

void dfs(int x)
{
	//树上倍增
	for(int i=1;;i++)
		if(fa[x][i-1])fa[x][i]=fa[fa[x][i-1]][i-1];
		else break;
	//深度处理 
	deep[x]=deep[fa[x][0]]+1;
	
	ol[++cnt]=x;//进入标记
	in[x]=cnt;
	
	int c=fir[x];
	while(c!=0)
	{
		dfs(v[c]);
		c=next[c];
	} 
	
	ol[++cnt]=x;//退出标记 
	out[x]=cnt;
}

bool cmp(node a,node b)//排序函数 
{
	return a.qu/len==b.qu/len ? a.qv<b.qv : a.qu/len < b.qu/len ; 
}

void px()
{
	sort(q+1,q+1+m,cmp);
}

void add(int x)
{
	if(++book2[ol[x]]!=2)book[col[ol[x]]]++;//颜色++
	else book[col[ol[x]]]--,book2[ol[x]]=0;//颜色减少并且退出这个点
	if(book[col[ol[x]]]==0)alcol--;
	if(book[col[ol[x]]]==1)alcol++;
}
void del(int x)
{
	if(--book2[ol[x]]!=-1)book[col[ol[x]]]--;
	else book[col[ol[x]]]++,book2[ol[x]]=1;//颜色增加并且点进入了
	if(book[col[ol[x]]]==0)alcol--;
	if(book[col[ol[x]]]==1)alcol++;
}

void moder()
{
	for(int i=1;i<=m;i++)
	{
		int graf;
		int l,r;
		//先判断两点的情况
		int x1=q[i].qu,x2=q[i].qv;
		if(deep[x1]>deep[x2]) 
		{
			int cha=deep[x1]-deep[x2];//x1需要向上提x1-x2位
			long long ttttt=log2(cha);
			for(int i=0;i<=ttttt;i++)
				if((1<<i)&cha)x1=fa[x1][i]; 
		}
		if(deep[x1]<deep[x2]) 
		{
			int cha=deep[x2]-deep[x1];//x2需要向上提x2-x1位
			long long ttt=log2(cha);
			for(int i=0;i<=ttt;i++)
				if((1<<i)&cha)x2=fa[x2][i]; 
		}
		if(x1!=x2)
		{
			//一起往上跳
			for(int i=20;i>=0;i--)  
				if(fa[x1][i]!=fa[x2][i])
					x1=fa[x1][i],x2=fa[x2][i];
			graf=fa[x1][0];//graf就是要求的公共祖先了 
			//算出实际在序列上访问的区间
			l=out[q[i].qv];
			r=in[q[i].qu];
		}else
		{
			graf=x1;//特判同一个祖先
			l=in[q[i].qu];
			r=in[q[i].qv];
		} 
		if(i==1)inl=l,inr=l-1; 
		while(inl<l)del(inl++);
		while(inl>l)add(--inl);
		while(inr<r)add(++inr);
		while(inr>r)del(inr--);
		if(book[col[graf]]==0)//两节点有公共祖先时,祖先不会被考虑到 
			ans[q[i].w]=alcol+1;
		else ans[q[i].w]=alcol;
	}
}

void o()
{
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
}

int main()
{
	read(); 
	dfs(1);//读入欧拉序以及树上倍增预处理 
	px();//将查询排序 
	moder();//开始moder找虐 
	o();
}
2020/7/25 17:50
加载中...