
保证k为偶数
题解
好题
性质题
这道题的理解关键就是P_{(i%k)+1},不是P_{i%(k+1)},也不是P_{i%k}+1
意思就是选一个点集P,依次走P_1,P_2……最后回到P_1的最大路径权值和
怎么办?
有一个关键的条件:k是偶数
我们考虑一下这个点集在树上的重心
如果我们想让得到的答案最大,我们一定会让这个集合反复横跳进过重心
然后发现,当路径重心的经过次数最大时,答案最大,且答案为点集中所有点到重心距离的两倍(画图感受一下。。。)
(注意一个点集在树上可能有多个重心,但是它们的答案算出来是一样的)
(有没有可能无法完成所有路径都经过重心的情况?没有,我们可以利用反证法,如果存在的话,当且仅当有从一个儿子上来的点的个数大于k/2,这与重心的定义不符)
于是我们可以二分答案?猜测重心的位置?
应该是点分治答案
首先猜全图的重心u
找到k个离u最远的点
如果有一个u的儿子pos的点的个数大于了k/2,我们就为了平衡,就会把下一个猜测的答案设为pos所在点分治区域的重心
(实际上就是在点分树上贪心)
如果当前点符合了条件,或者已经到了最小的分治区域,那么直接计算输出答案
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
#define LL long long
int n,k;
int fir[N],to[2*N],nxt[2*N],cd[2*N],cnt;
int siz[N],con[N],from[N];LL dis[N];
int all,mi,nrt;bool vis[N];
void adde(int a,int b,int c)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cd[cnt]=c;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cd[cnt]=c;
}
void find(int u,int ff)
{
int mx=0;siz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=ff&&!vis[v]){
find(v,u);
siz[u]+=siz[v];
mx=max(mx,siz[v]);
}
}
mx=max(all-siz[u],mx);
if(mx<mi)mi=mx,nrt=u;
}
int getrt(int u,int sz){mi=0x3f3f3f3f;nrt=0;all=sz;find(u,0);return nrt;}
void dfs(int u,int ff,int pre)
{
from[u]=pre;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=ff){
dis[v]=dis[u]+cd[p];
dfs(v,u,!pre?v:pre);
}
}
}
int id[N];
bool cmp(int x,int y){return dis[x]>dis[y];}
void solve(int u)
{
vis[u]=1;
dis[u]=0;dfs(u,0,0);
nth_element(id+1,id+k,id+n+1,cmp);
memset(con,0,sizeof(con));
int pos=0;
for(int i=1;i<=k;i++){
int ff=from[id[i]];
if(con[ff]==k/2){pos=ff;break;}
con[ff]++;
}
if(pos&&!vis[pos]){solve(getrt(pos,siz[pos]));}
else{
sort(id+1,id+n+1,cmp);
memset(con,0,sizeof(con));
LL ans=0;int ct=0;
for(int i=1;i<=n;i++){
int ff=from[id[i]];
if(con[ff]==k/2)continue;
con[ff]++;ans+=dis[id[i]];
if((++ct)==k)break;
}
printf("%lld\n",2ll*ans);
}
}
int main()
{
int i,u,v,w;
scanf("%d%d",&n,&k);
for(i=1;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);id[i]=i;
}
id[n]=n;
solve(1);
}
浙公网安备 33010602011771号