P3363 Cool loves jiaoyi

P3363 Cool loves jiaoyi

我常常追忆过去。

题意

给你一棵 \(n\) 个点的树。给你 \(m\) 个链 \((u,v)\)。选出 \(k\) 个链,使得存在一个点 \(x\)\(k\) 个链里都有。一个方案的代价是选中的最长链的长度减去最短链的长度。求方案的最小代价。

\(n \le 5 \times 10^4, k \le m \le 10^4\)

思路

看到数据范围容易想到 recall。初始想法是使用 bitset 存每个链有哪些点。

但是发现很难利用 bitset 的按位与、按位或。

想到如果常数不大,\(m^2\) 甚至 \(nm\) 其实也是可以过的。


思考一下我们要怎样做,需要怎样的常数。

首先 \(O(nm)\) 暴力遍历每条链,在这条链的 bitset 把这条链有的点设为 \(1\)

然后因为要求最小长度差,我们把链按照链长排序,然后双指针做。

左端点是 \(l\),找到最小的 \(r\) 使得可以在编号为 \([l,r]\) 的链中选出 \(k\) 条合法的链。那么这种选法的代价就是 \(len_r - len_l\)

所以要维护当前 \([l,r]\) 有没有合法方案。

维护每个点在 \([l,r]\) 中一共出现了几次,如果存在一个点出现了 \(k\) 次,就存在合法方案(选择包含这个点的 \(k\) 个链即可)。

时间复杂度是 \(O(nm)\) 的。而且只有一些简单的遍历和加减法,所以是很快的。


最慢点只需要 200+ms,绰绰有余。

这里的 bitset 其实没有优化时间,主要是优化空间的,因为即使是 \(m^2\) 个 int 也是开不下的,但是 \(nm\) 个 bit 却可以开下。

在比较随机的数据下,只需要跑几十毫秒,非常快。因为 \(n\) 很难跑满。当然遍历 bitset 的时候最好使用 _Find_next(x) 之类。

最初我还建了虚树,可以把 \(n\) 降到 \(4m\) 级别。但是所有测试点加起来也就优化零点零几秒,没有什么用。

顺带一提,这题好像没有 \(-1\) 的数据。

code

非常好写。bitset 还是太牛了。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
	constexpr int N=5e4+7,M=1e4+7;
	int n,m,k;
	struct pii {
		int u,v;
	}a[M];
	vector<int> to[N];
	int fa[N],dep[N];
	void dfs(int u,int f) {
		fa[u]=f;
		dep[u]=dep[f]+1;
		for(int v : to[u]) if(v^f) {
			dfs(v,u);
		}
	}
	int rt;
	bitset<N> li[M];
	int siz[M];
	int cnt[N];
	int ans;
	int p[N],ip[N];
	bool cmp(int x,int y) { return siz[x]<siz[y]; }
	void main() {
		sf("%d%d%d",&n,&m,&k);
		rep(i,1,n-1) {
			int u,v;
			sf("%d%d",&u,&v);
			to[u].push_back(v), to[v].push_back(u);
		}
		rep(i,1,m) {
			int u,v;
			sf("%d%d",&u,&v);
			a[i] = {u,v};
		}
		dfs(1,0);
		rep(i,1,m) {
			int u=a[i].u, v=a[i].v;
			if(dep[u]<dep[v]) swap(u,v);
			while(dep[u]>dep[v]) li[i][u]=1, siz[i]++, u=fa[u];
			while(u!=v) li[i][u]=1, li[i][v]=1, siz[i]+=2, u=fa[u], v=fa[v];
			li[i][u]=1;
			p[i]=i;
		}
		sort(p+1,p+m+1,cmp);
		ans=n+1;
		int r=0;
		rep(i,1,m) {
			while(r<n && !cnt[0]) {
				++r;
				for(int x=li[p[r]]._Find_first();x<=n;x=li[p[r]]._Find_next(x)) if(++cnt[x]==k) ++cnt[0];
			}
			if(!cnt[0]) break;
			ans=min(ans,siz[p[r]]-siz[p[i]]);
			for(int x=li[p[i]]._Find_first();x<=n;x=li[p[i]]._Find_next(x)) if(cnt[x]--==k) --cnt[0];
		}
		if(ans==n+1) ans=-1;
		pf("%d\n",ans);
	}
}
int main() {
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#endif
	wing_heart :: main();
}
posted @ 2025-10-10 16:13  wing_heart  阅读(12)  评论(0)    收藏  举报