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();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/19133220

浙公网安备 33010602011771号