有 k k k 棵树,每棵树有 n n n 个点,对于所有的点对 ( s , t ) (s,t) (s,t)( 1 ≤ s , t ≤ n 1 \leq s,t \leq n 1≤s,t≤n),求出有多少个点在每棵树的 s s s 到 t t t 的链上都存在。
(以防表述不清,给一下传送门
n , k ≤ 500 n,k \leq 500 n,k≤500
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("");
}
}