[集训队互测 2024] 串联

我是蒟蒻,我从难题来,看了 \(inf\) 分钟的题解。遂总结。

题目思路

点分治。

在 getans() 中从 \(u\) 出发,往子树上走,记录每个路径的信息:记 \(mn\) 为最小的 \(a\),\(sum\) 为路径上 \(b\) 之和,\(id\) 为染色的编号。为什么要染色呢?因为我们规定了路径必须从 \(u\) 的一个子节点走到 \(u\) 再往 \(u\) 的另一个子节点。两个路径只在一个子树内是不合法的,因此染色是为了区分当前路径在哪个子树内。

接下来要考虑怎么处理要求 \(a\) 的最小值。如果前面记录的都是大于当前 \(a\) 的不就好了? 那就将 dfs 得到的所有路径存起来,按照 \(a\) 降序排序。对于有关 \(b\) 的约束,先不妨设当前路径有 \(A\),\(B\)(含义分别为 \(a\) 的最小值和 \(b\) 之和),还有一个期望的路径有 \(B'\)。

由题意得:\((B'+B+b_u) \times A \ge V\)。

化简:\(B' \ge \lceil \frac{V}{A} \rceil + b_u -B\)。为什么要向上取整?自行思考。

换言之,我们希望找到一些前面的出现过路径(也就是满足 \(A' \ge A\)的)满足上式。这里使用后缀树状数组。

查询时,用它找前面出现过的合法 \(B'\) 就好了。

插入又有一个难点呜呜呜我菜飞了。刚刚提到了两个路径都在一个子树内不合法,所以树状数组要记录最小值和次小值。

不要忘记离散化。

AC Code

#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x&(-x))
using namespace std;
const int N=2e5+5,inf=1e18;
int n,m,a[N],b[N],sz[N],tot,mx[N],st[N],ans=1e18,rt,tp;
bool vis[N];
vector<int>g[N];
struct path{
	int mn,sum,id;
};
bool cmp(const path& s1,const path& s2){
	if(s1.mn!=s2.mn)	return s1.mn>s2.mn;
	return s1.sum>s2.sum;
}
vector<path>q;
struct BT{
	int mn,mmn,co,col;
}tr[N],MX={inf,inf,0,0};
//tr下标:离散化后的B'(路径b的和)
BT operator +(BT x,BT y){
	if(x.mn>y.mn)	swap(x,y);
	if(x.co==y.co){
		if(x.mmn>y.mmn){
			x.mmn=y.mmn;x.col=y.col;
		}
	}
	else if(x.mmn>y.mn){
		x.mmn=y.mn;x.col=y.co;
	}
	return x;
}
BT query(int x){
	BT res=MX;
	while(x<=tp){
		res=res+tr[x];
		x+=lowbit(x);
	}return res;
}
void add(int x,BT num){
	while(x){
		tr[x]=tr[x]+num;
		x-=lowbit(x);
	}
}
void getroot(int u,int fa){
	sz[u]=1;mx[u]=0;
	for(int v:g[u]){
		if(vis[v]||v==fa)	continue;
		getroot(v,u);
		sz[u]+=sz[v];
		mx[u]=max(mx[u],sz[v]);
	}
	mx[u]=max(mx[u],tot-sz[u]);
	if(mx[u]<mx[rt])	rt=u;
}
void dfs(int u,int fa,int A,int B,int id){
	A=min(A,a[u]);
	B+=b[u];
	q.push_back({A,B,id});
	for(int v:g[u]){
		if(v==fa||vis[v])	continue;
		dfs(v,u,A,B,id);
	}
}
void getans(int u){
	q.clear();
	for(int v:g[u]){
		if(vis[v])	continue;
		dfs(v,u,a[u],b[u],v);
	}
	if(q.empty())	return ;
	sort(q.begin(),q.end(),cmp);
	tp=0;
	for(auto it:q)	st[++tp]=it.sum;
	sort(st+1,st+tp+1);
	tp=unique(st+1,st+tp+1)-st-1;
	fill(tr,tr+tp+1,MX);
	for(auto it:q){
		if(it.sum>=(m+it.mn-1)/it.mn)	ans=min(ans,it.sum);
		int id=lower_bound(st+1,st+tp+1,(m+it.mn-1)/it.mn-it.sum+b[u])-st;//向上取整计算技巧 
		if(id<=tp){
			auto z=query(id);
			if(z.mn<inf)	ans=min(ans,it.sum+(z.co==it.id?z.mmn:z.mn)-b[u]);
		}
		id=lower_bound(st+1,st+tp+1,it.sum)-st;
		add(id,{it.sum,inf,it.id,0});
	}
}
void divide(int u){
	vis[u]=1;
	getans(u);
	for(int v:g[u]){
		if(vis[v])	continue;
		tot=sz[v];rt=0;
		getroot(v,u);
		getroot(rt,0);
		divide(rt);
	}
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)	cin>>a[i]>>b[i];
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
		if(a[i]*b[i]>=m)	ans=min(ans,b[i]);
	}
	mx[rt=0]=inf;tot=n;
	getroot(1,0);
	getroot(rt,0);
	divide(rt);
	cout<<ans;
	return 0;
}
posted @ 2026-08-25 10:18  深申  阅读(12)  评论(0)    收藏  举报