codeforces1060E(若点思考困难就可以考虑边,解题时将数学关系列出来)
解题思路
可以用公式将关系表示出来,然后再进行计算,很显然任意两点对答案的
贡献是两点距离除2然后向上取整,于是有公式
\[{\lceil \frac{两点距离}{2} \rceil}=\frac{两点距离+两点距离 \%2}{2}
\]
故将所有点对都算一遍就有
\[ ans=\sum_{i=1}^{n}\sum_{j=i}^{n} (\frac{i和j的距离+两点距离 \%2}{2})
\]
对于左边的部分,所有i,j的距离和可以只看边,因为一个边的左边的点一定会经过该边与右边的点相连,经过该边的总次数是左边的点*右边的点,也就是子树大小size[x] x (n-size[x])
然后是右边的部分,那就是距离为奇数的点对数,然后发现若是对树进行奇偶分层,同奇同偶的节点之间的距离就为偶数,反之则为偶数,故只需要计算在奇数层的点数×偶数层的点数即可
代码
#include<bits/stdc++.h>
#define int long long
#define inf 1e18
#define MOD 1000000007
using namespace std;
void solve(){
int n;
cin>>n;
vector<vector<int> >a(n+1);
int u,v;
for(int i=1;i<n;i++){
cin>>u>>v;
a[u].push_back(v);
a[v].push_back(u);
}
int ans=0;
int cnt[2]={0};
vector<int>dep(n+1),sze(n+1);
auto dfs=[&](auto self,int x,int y)->void{
dep[x]=dep[y]+1;
cnt[dep[x]%2]++;
sze[x]=1;
for(auto i:a[x]){
if(i==y)continue;
self(self,i,x);
sze[x]+=sze[i];
ans+=(sze[i]*(n-sze[i]));
}
};
dfs(dfs,1,0);
ans+=cnt[1]*cnt[0];
cout<<ans/2ll<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cout.tie(0),cin.tie(0);
int test=1;
while(test--)
solve();
return 0;
}
int
反思总结
-
首先是以后再解决问题时可以考虑将数据概念之间的关系用公式表达出来,然后再对公式进行运算与变换
-
对于点不太好思考的话就可以思考边的关系