【VKOSHP 19 L】Time Travel 题解

题目大意

  有 k k k 棵树,每棵树有 n n n 个点,对于所有的点对 ( s , t ) (s,t) (s,t) 1 ≤ s , t ≤ n 1 \leq s,t \leq n 1s,tn),求出有多少个点在每棵树的 s s s t t t 的链上都存在。
  (以防表述不清,给一下传送门
   n , k ≤ 500 n,k \leq 500 n,k500
  2s

\\
\\
\\

题解

  一开始怎么想都是 4 次方的复杂度,尝试了暴力+bitset 也 T 了。

  这题的关键是要注意到一个性质:在一棵树上, x x x y y y 经过 c c c,等价于 d i s x , y = d i s x , c + d i s c , y dis_{x,y}=dis_{x,c}+dis_{c,y} disx,y=disx,c+disc,y
  而且 d i s x , y dis_{x,y} disx,y 是小于等于 d i s x , c + d i s c , y dis_{x,c}+dis_{c,y} disx,c+disc,y 的。
  那么 x x x y y y 在所有树上都经过 c c c,就等价于 ∑ d i s x , y = ∑ d i s x , c + ∑ d i s c , y \sum dis_{x,y}=\sum dis_{x,c}+\sum dis_{c,y} disx,y=disx,c+disc,y

  那么就 O ( k n 2 ) O(kn^2) O(kn2) 预处理 ∑ d i s x , y \sum dis_{x,y} disx,y,然后 O ( n 3 ) O(n^3) O(n3) 枚举点对与中间点,判断合不合法。

代码

#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int maxn=505;

int n,k;

int tot[maxn],go[maxn][2*maxn],nxt[maxn][2*maxn],f1[maxn][maxn];
void ins(int ty,int x,int y)
{
     
	go[ty][++tot[ty]]=y;
	nxt[ty][tot[ty]]=f1[ty][x];
	f1[ty][x]=tot[ty];
}

int dis[maxn][maxn];
void dfs(int ty,int tp,int k,int last,int s)
{
     
	dis[tp][k]+=s;
	for(int p=f1[ty][k]; p; p=nxt[ty][p]) if (go[ty][p]!=last) dfs(ty,tp,go[ty][p],k,s+1);
}

int main()
{
     
	scanf("%d %d",&n,&k);
	fo(i,1,k)
		fo(j,1,n-1)
		{
     
			int x,y;
			scanf("%d %d",&x,&y);
			ins(i,x,y), ins(i,y,x);
		}
	
	fo(i,1,k)
		fo(j,1,n) dfs(i,j,j,0,0);
	
	fo(a,1,n)
	{
     
		fo(b,1,n)
		{
     
			int sum=0;
			fo(c,1,n) sum+=(dis[a][c]+dis[c][b]==dis[a][b]);
			printf("%d ",sum);
		}
		puts("");
	}
}

你可能感兴趣的:(算法_神奇的脑洞)