题解:P3563 [POI 2013] POL-Polarization
wangzichen20240101 · · 题解
题目给出了一棵树,然后要求我们把所有无向边替换成有向边,再求出可到达点对的最少和最大数量。
首先,最小值的答案是显而易见的。因为不管所有的构造方式,令一条边从
所以既然是相乘,两个乘数的差值肯定是越大越好,所以不妨让所有点都指向某个节点,这时答案为
那么反过来,求最大值的思想就是使乘数的差值尽可能小,即我们选出来的关键点,满足通向它的点与被它通向的点的差值最小。(这里的“通向”仅代表方向,而非直接连通)
所以那个关键点就是重心,因为只有这样我们才可以更自由的调配所有与这个点直接连通的点与关键点的方向。
我们以重心
我们维护
若最后装了
但这样的时间复杂度为
这里简单证明一下,若无二进制拆分,那么有
当二进制拆分后,为了使二进制拆分尽量不发挥作用,我们构造序列
因此总时间复杂度为
注意到
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2.5e5 + 10;
int n,rt,ans,pos,sz[N],mx[N],cnt[N];
vector<int> e[N];
bitset<N> dp;
void dfs(int u,int fa){
mx[u] = 0;
sz[u] = 1;
for(int v : e[u]){
if(v == fa) continue;
dfs(v,u);
sz[u] += sz[v];
mx[u] = max(mx[u],sz[v]);
}
mx[u] = max(mx[u],n - sz[u]);
if(!rt || mx[u] < mx[rt]) rt = u;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n;
dp[0] = 1;
for(int i = 1;i < n;++i){
int u,v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
dfs(rt,0);
ans = -sz[rt];
for(int i = 1;i <= n;++i) ans += sz[i];//子树内贡献
for(int v : e[rt]) ++cnt[sz[v]];
for(int i = 1;i <= n / 2;++i){
while(cnt[i] > 2){
cnt[i] -= 2;
++cnt[i << 1];
}
}
for(int i = 1;i <= n;++i){
while(cnt[i]--) dp |= (dp << i);
}
for(int i = (n >> 1);i >= 0;--i){
if(dp[i]){
pos = i;
break;
}
}
cout << n - 1 << ' ' << ans + pos * (n - pos - 1);
return 0;
}