题解:CF2241E Fair and Square
比较套路的组合加上非常基础的换根。
首先将条件转化一下,对于树上的三个点呢,其位置有两种可能,一种是位于一条路径上的,另一种是分散的(类似氨气分子立体图示,每个氢原子就是那三个点)。
对于第一种情况,我们不妨设
注意上面这个式子要除以
然后考虑第二种情况。第二种情况相当于找一个点,然后在这个点的子树中找出三颗子树,然后每一颗子树上分别取出一个儿子,这也是满足条件的,例如样例中的点
假设点
你可以使用数学的方法也可以展开
然后加上
展开整理得
同理,这个值要除以
于是我们进行完了两部分得计数,但是我们要求任意一个节点为根时的其儿子子树大小,这个也很简单,就是基础的换根。
回溯时要变为原来的值,然后这题就做完了,时间复杂度为
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+10;
int t,n,a[N],sz[N],ans,u,v;
vector<int>e[N];
bool f(int x){
int y=sqrt(x);
return y*y==x;
}
void dfs(int x,int fath){
sz[x]=1;
for(auto it:e[x]){
if(it==fath)continue;
dfs(it,x);
sz[x]+=sz[it];
}
}
void DP(int x,int fath){
if(f(a[x])){
int sum=0,add=0,add1=0;
sum+=(n-1)*(n-1);
for(auto it:e[x]){
sum-=sz[it]*sz[it];
add+=sz[it]*sz[it];
add1+=sz[it]*sz[it]*sz[it];
}
ans+=sum/2;
if(e[x].size()>2){
ans+=((n-1)*(n-1)*(n-1)-3*(n-1)*add+2*add1)/6;
}
}
for(auto it:e[x]){
if(it==fath)continue;
int tmp=sz[x],mid=sz[it];
sz[x]=n-sz[it];
sz[it]=n;
DP(it,x);
sz[x]=tmp;
sz[it]=mid;
}
}
signed main(){
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)e[i].clear();
for(int i=1;i<n;++i){
cin>>u>>v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
ans=0;
dfs(1,0);
DP(1,0);
cout<<ans<<"\n";
}
return 0;
}