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;
}
posted @ 2025-05-15 10:16  zxh923  阅读(51)  评论(0)    收藏  举报