洛谷 P6626 [省选联考 2020 B 卷] 消息传递

洛谷 P6626 [省选联考 2020 B 卷] 消息传递

Problem

原题传送门

给一棵有\(n\)个节点的树。有\(m\)个询问,每次给出一对\(x,k\)表示查询到点\(x\)的距离为\(k\)的点有多少个。

\(T\)组数据。

\(1\sim3:1\le n,m\le10^3\\\)

\(4\sim6:1\le n,m\le 10^5,k\le 20\\\)

\(7\sim10:1\le n,m\le 10^5\\\)

\(\forall:1\le T\le5,1\le x\le n,0\le k\le n\).

Solution1

暴力不必多说,写个\(dfs/bfs\)就有\(30pts\),时间复杂度是\(O(n^2)\)

优化

对点分治还不熟练,所以先来学个另类做法。

将询问离线,然后用换根法进行处理。

将根从\(x\)换为\(y\)时,\(y\)的子树中所有节点深度减一,其他点的深度都加一。用桶来记录深度。

先用树链剖分将树变为序列方便区间操作。

每一次换根,我们都需要做两次区间修改,同时还要维护一个桶。所以不能用线段树,但是可以用分块。对每个块开一个桶即可。

当换根到有询问的点时,暴力拿出对应块的桶进行计数即可。

时间复杂度\(O(Tn\sqrt n)\),空间复杂度\(O(n\sqrt n)\)

理论可以通过,实际需要开\(O2\).

Code1

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<limits.h>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std;

template<class T>inline void read(T& x)
{
	char ch=getchar();
	int fu;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') fu=-1,ch=getchar();
	x=ch-'0'; ch=getchar();
	while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar(); }
	x*=fu;
}
inline int read()
{
	int x=0,fu=1;
	char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') fu=-1,ch=getchar();
	x=ch-'0'; ch=getchar();
	while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar(); }
	return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
	int g=0;
	if(x<0) x=-x,putchar('-');
	do { G[++g]=x%10; x/=10; } while(x);
	for(int i=g; i>=1; --i)putchar('0'+G[i]); putchar('\n');
}
int T;
#define N 100010
#define M 400
//graph
int head[N],ver[N<<1],nxt[N<<1],cnt;
IL void insert(int x,int y)
{
	nxt[++cnt]=head[x];
	head[x]=cnt;
	ver[cnt]=y;

	nxt[++cnt]=head[y];
	head[y]=cnt;
	ver[cnt]=x;
}
//ask
struct node
{
	int k,op,nxt;
}a[N];
int top;
int hh[N];
IL void insert(int x,int k,int op)
{
	a[++top].k=k;
	a[top].op=op;
	a[top].nxt=hh[x];
	hh[x]=top;
}
//pre
int dis[N],sze[N],dfn[N],dfncnt,m,n;
int b[N];
void dfs(int x,int f)
{
	dis[x]=dis[f]+1;
	sze[x]=1;
	dfn[x]=++dfncnt;
	b[dfncnt]=dis[x];
	for(int i=head[x];i;i=nxt[i])
	{
		if(ver[i]==f) continue;
		dfs(ver[i],x);
		sze[x]+=sze[ver[i]];
	}
}
//block
int len,s;
int L[M],R[M],belong[N];
int lazy[M],c[M][N];
void change(int x,int y,int v)
{
	re int xx=belong[x],yy=belong[y],i,j;
	if(xx==yy)
	{
		for(i=L[xx];i<=R[xx];i++)
		{
			c[xx][b[i]]--;
			b[i]+=lazy[xx];
		}
		lazy[xx]=0;
		for(i=x;i<=y;i++)
		{
			b[i]+=v;
		}
		for(i=L[xx];i<=R[xx];i++)
		{
			c[xx][b[i]]++;
		}
	}
	else
	{
		for(i=xx+1;i<=yy-1;i++)
		{
			lazy[i]+=v;
		}
		for(i=L[xx];i<=R[xx];i++)
		{
			c[xx][b[i]]--;
			b[i]+=lazy[xx];
		}
		for(i=L[yy];i<=R[yy];i++)
		{
			c[yy][b[i]]--;
			b[i]+=lazy[yy];
		}
		lazy[xx]=lazy[yy]=0;
		for(i=x;i<=R[xx];i++)
		{
			b[i]+=v;
		}
		for(i=L[yy];i<=y;i++)
		{
			b[i]+=v;
		}
		for(i=L[xx];i<=R[xx];i++)
		{
			c[xx][b[i]]++;
		}
		for(i=L[yy];i<=R[yy];i++)
		{
			c[yy][b[i]]++;
		}
	}
}
int ans[N];
void dfs1(int x,int f)
{
	re int i,j;
	for(i=hh[x];i;i=a[i].nxt)
	{
		for(j=1;j<=s;j++)
		{
			if(a[i].k+1-lazy[j]>=0) ans[a[i].op]+=c[j][a[i].k+1-lazy[j]];
		}
	}
	for(i=head[x];i;i=nxt[i])
	{
		if(ver[i]==f) continue;
		for(j=1;j<=s;j++) lazy[j]++;
		change(dfn[ver[i]],dfn[ver[i]]+sze[ver[i]]-1,-2);
		dfs1(ver[i],x);
		for(j=1;j<=s;j++) lazy[j]--;
		change(dfn[ver[i]],dfn[ver[i]]+sze[ver[i]]-1,2);
	}
}
int main()
{
	T=read();
	int i,j,x,k;
	while(T--)
	{
		//init
		memset(head,0,sizeof(head));cnt=0;
		memset(hh,0,sizeof(hh));top=0;
		dfncnt=0;
		memset(lazy,0,sizeof(lazy));
		memset(ans,0,sizeof(ans));
		//work
		n=read();
		m=read();
		for(i=1;i<n;i++) insert(read(),read());
		dfs(1,0);
		for(i=1;i<=m;i++)
		{
			x=read();
			k=read();
			insert(x,k,i);
		}
		len=sqrt(n);
		s=n/len;
		for(i=1;i<=s;i++)
		{
			L[i]=n/s*(i-1)+1;
			R[i]=n/s*i;
		}
		R[s]=n;
		for(i=1;i<=s;i++)
		{
			for(int j=L[i];j<=R[i];j++)
			{
				belong[j]=i;
			}
		}
		for(i=1;i<=s;i++)
		{
			lazy[i]=0;
			for(int j=L[i];j<=R[i];j++)
			{
				c[i][b[j]]++;
			}
		}
		dfs1(1,0);
		for(i=1;i<=m;i++) cout<<ans[i]<<endl;
		for(i=1;i<=s;i++)
		{
			lazy[i]=0;
			for(j=L[i];j<=R[i];j++)
			{
				c[i][b[j]]=0;
			}
		}
	}

	return 0;
}

Solution2

再看题目像点分治,但是这里指定了目标点。

猜想把询问离线,那就考虑怎么分别计算贡献。

Code2

posted @ 2021-03-05 10:40  Vanilla_chan  阅读(175)  评论(0编辑  收藏  举报