QOJ 2065 Cyclic Distance
首先对于一条边,可以注意到一个性质,假设一条边下端子树内有 \(x\) 个点,则它的最大经过次数危 \(2\times \min(x,k-x)\)。
其实对于任意的这样的边,这个最大值都是能达到的。
于是我们自然的想到一个 dp。设 \(f_{u,j}\) 表示 \(u\) 子树内,选了 \(j\) 个点,\(u\) 子树内的最大贡献。
注意到两个东西的背包合并实际上做的是闵可夫斯基和。再注意到这个 \(f\) 实际上是凸的。于是直接维护差分即可。
因为 \(f\) 是凸的,所以它的差分是单调递减的。考虑用两个堆维护前半部分和后半部分(这个半部分的划分是基于 \(\dfrac{k}{2}\) 的)。
考虑为什么要这么划分,因为观察一开始的那个式子,选一个点就相当于让 \(\min\) 里的一部分加上 \(1\),再让另一部分减去 \(1\)。
于是我们只需要对两个堆整体加即可,这个东西直接记一个 tag 即可。然后就是合并两个子树,直接启发式合并即可。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 200005
#define pii pair<int,int>
#define x first
#define y second
#define mod 998244353
#define inf 2e18
using namespace std;
int T=1,n,m,p,id[N];
vector<pii>e[N];
struct pq{
priority_queue<int,vector<int>,greater<int>>q1;
priority_queue<int>q2;
int t1=0,t2=0;
void work(){
while(q1.size()>p){
q2.push(q1.top()+t1-t2);
q1.pop();
}
}
void ins(int x){
q1.push(x-t1);
work();
}
void rec(int x){
t1+=x;
if(m%2==1&&!q2.empty()){
int cur=q2.top()+t2;
q2.pop();
t2-=x;
q2.push(cur-t2);
}
else t2-=x;
}
int size(){
return q1.size()+q2.size();
}
int top(){
return !q2.empty()?q2.top()+t2:q1.top()+t1;
}
void pop(){
!q2.empty()?q2.pop():q1.pop();
}
void init(){
t1=t2=0;
while(!q1.empty())q1.pop();
while(!q2.empty())q2.pop();
}
}q[N];
void add(int a,int b,int c){
e[a].push_back({b,c});
}
void merge(int &u,int v){
if(q[u].size()<q[v].size())swap(u,v);
int siz=q[v].size();
while(siz--){
q[u].ins(q[v].top());
q[v].pop();
}
}
void dfs(int u,int fa){
id[u]=u;
q[id[u]].init();
for(auto it:e[u]){
int j=it.x,w=it.y;
if(j==fa)continue;
dfs(j,u);
q[id[j]].rec(w<<1);
merge(id[u],id[j]);
}
q[id[u]].ins(0);
}
void solve(int cs){
cin>>n>>m;
p=m>>1;
for(int i=1;i<=n;i++){
e[i].clear();
}
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
dfs(1,0);
vector<int>res;
for(int i=1;i<=n;i++){
res.push_back(q[id[1]].top());
q[id[1]].pop();
}
sort(res.begin(),res.end(),greater<int>());
int sum=0;
for(int i=0;i<m;i++){
sum+=res[i];
}
cout<<sum<<'\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}

浙公网安备 33010602011771号