当前位置: 代码迷 >> 综合 >> 【CodeForces】【思维】【DP】1060E-Sergey and Subway
  详细解决方案

【CodeForces】【思维】【DP】1060E-Sergey and Subway

热度:96   发布时间:2023-11-21 07:01:33.0

CodeForces 1060E Sergey and Subway

◇题目传送门◆

前言

感谢Tiw_Air_OAO dalao对我的提示,使我从迷茫走向AC。

题目大意

给定一张有NNN个节点的树形图GGG,若图上存在三个点u,vu,vu,v,且满足u,vu,vu,v之间相距距离为222,则可以在u,vu,vu,v之间连一条边。问此时所有点对间最短距离和。

思路

若我们按照暴力模拟的方式,则必定超时。

考虑两点u,vu,vu,v,若它们在原图上的距离sss为偶数,则它们在新图上的距离为s2\frac{s}{2}2s?,若它们的距离为奇数,则它们在新图上的距离为s+12\frac{s+1}{2}2s+1?

换句话说,距离之和为∑?su,v2?(u∈G,v∈G)\sum\lceil\frac{s_{u,v}}{2}\rceil(u\in G,v\in G)?2su,v???(uG,vG)

这样时间复杂度就降低到了O(n2)O(n^2)O(n2)

但是对于这道题,时间复杂度还是太高了。。。

所以,我们将式子展开一下。不难发现在su,vs_{u,v}su,v?中,有一些奇数的路径多出的12\frac{1}{2}21?也对答案做了贡献。考虑将这些12\frac{1}{2}21?提出来。就可以发现,答案就变为:(所有路径长度之和+奇数路径个数)/2。

首先是求所有路径长度之和。我们考虑以uuu为根的子树,其大小为sus_usu?,如下图:

不难发现每条边经过的次数为su×(N?su)s_u\times(N-s_u)su?×(N?su?)。则所有路径长度之和=∑su×(N?su)(u∈G)\sum{s_u\times(N-s_u)}(u\in G)su?×(N?su?)(uG)。所以这个可以DFS一次解决。

接下来是求奇数路径个数。考虑两点u,vu,vu,v,若点uuu在奇数层,vvv在偶数层,则两点间距离必定为奇数,所以,奇数路径个数就等于奇数层点数*偶数层点数,这东西也可以在求所有路径长度之和时顺带着做了。

注意数据可能炸int,请使用long long

正解代码

#include<cstdio> #include<vector> #include<algorithm> using namespace std;typedef long long ll; const int Maxn=200000;int N; vector<int> G[Maxn+5]; inline void addedge(int u,int v) {
     G[u].push_back(v);G[v].push_back(u); }ll ans,cnt[3]; ll DFS(int u,int fa,int col) {
     cnt[col]++;//记录奇数层和偶数层点的个数//cnt[0]-奇数点数,cnt[1]-偶数点数ll siz=1;for(int i=0;i<G[u].size();i++) {
     int v=G[u][i];if(v==fa)continue;siz+=DFS(v,u,col==1?0:1);}ans+=siz*(N-siz);return siz; }int main() {
     #ifdef LOACLfreopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#endifscanf("%d",&N);for(int i=1;i<N;i++) {
     int u,v;scanf("%d %d",&u,&v);addedge(u,v);}DFS(1,0,0);printf("%lld\n",(ans+cnt[0]*cnt[1])/2);return 0; }