赛前模拟1-20250825(总结)
Part 1 赛时
T1
首先我们可以发现测试点分为两类:
-
n=m+1$ ,这只是一个普通的树,我们可以对每一个节点的子节点,按编号排序,进行贪心,时间复杂度$O(N\ logN)$,$\color{green} Accepted -
n=m$ ,很明显,这是一个基环树,当时我已经想到了可以挨个断边,但当时我认为这**太暴力了!**,于是写了一个神奇的贪心,喜提$\color {red} 64tps
T2
| 看看这数据规模与约定: | 测试点编号 | 分支不超过 |
||||
|---|---|---|---|---|---|---|
| 否 | 否 | 是 | ||||
| 否 | 是 | 是 | ||||
| 是 | 否 | 否 | ||||
| 否 | 否 | 是 | ||||
| 是 | 否 | 否 | ||||
| 否 | 否 | 否 | ||||
| 是 | 否 | 否 | ||||
| 是 | 否 | 否 | ||||
| 否 | 是 | 是 | ||||
| 否 | 是 | 是 | ||||
| 否 | 是 | 是 | ||||
| 否 | 否 | 是 | ||||
| 否 | 否 | 是 | ||||
| 否 | 否 | 是 | ||||
| 否 | 否 | 是 | ||||
| 否 | 否 | 是 | ||||
| 否 | 否 | 否 | ||||
| 否 | 否 | 否 | ||||
| 否 | 否 | 否 | ||||
| 否 | 否 | 否 |
简单的数了一下,如果把所有特殊点加上,可以拿到
- m=1,这个十分明显,m=1代表只用出现一条赛道,如果要让这条赛道的长度最长,这条赛道其实就是这个树的直径,十分简单
- ai=1,这是一个菊花图:
对于这种情况,我们发现每一条赛道至多为2条边,如果
- ai=bi+1这是一条链,我们可以使用二分,每一次依次加边,直到>=mid,看能不能全部分配够。
T3
我在考场上,看到了这个:
| 测试点编号 | ||
|---|---|---|
A3 |
||
C3 |
||
A3 |
||
C3 |
||
A3 |
||
C3 |
||
A1 |
||
A2 |
||
A3 |
||
B1 |
||
C1 |
||
C2 |
||
C3 |
于是我先写了所有A的骗分,但时间复杂度算错了,只拿了
T4
暴力
Part 2 赛后
T1
赛后我很快发现暴力断边的时间复杂度
T2
肉眼可得: 我们可以使用二分答案,check的内容为长度大于等于mid是否能超过m个。
这道题我们可以发现每一个赛道可以分解为两条链,我们记录一下沿着每一个当前节点的子节点向下的最长链,为了满足要求,我们应当选择两条链使其最接近mid,如果剩下一些链没写,传上去。
(当然,如果一条链就大于mid了,便直接ans++)
这一段可以使用set实现,但我用的是数组。
for(int i=1;i<cnt;i++){
if(vis[i]) continue;
int it=lower_bound(a+i+1,a+cnt+1,mid-a[i])-a;
if(it==cnt+1) continue;
while(vis[it]&&it<cnt) it++;
if(!vis[it]&&a[i]+a[it]>=mid)
ans++,vis[i]=vis[it]=1;
}
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
/*!@#$%^&*!@#$%^&*~~优美的分界线~~*&^%$#@!*&^%$#@!*/
const int N=5e4+5;
int n,m;
int ans,mid;
int a[N],b[N],vis[N];
int tot,head[N],ver[N<<1],nxt[N<<1],edg[N<<1];
/*!@#$%^&*!@#$%^&*~~优美的分界线~~*&^%$#@!*&^%$#@!*/
void add(int a,int b,int c){
ver[++tot]=b,edg[tot]=c;
nxt[tot]=head[a],head[a]=tot;
}
void dfs(int x,int fa){
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
dfs(y,x);
}
int cnt=0;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i],w=edg[i];
if(y==fa) continue;
if(b[y]+w>=mid) ans++;
else a[++cnt]=b[y]+w;
}
sort(a+1,a+cnt+1);
for(int i=1;i<=cnt;i++)
vis[i]=0;
for(int i=1;i<cnt;i++){
if(vis[i]) continue;
int it=lower_bound(a+i+1,a+cnt+1,mid-a[i])-a;
if(it==cnt+1) continue;
while(vis[it]&&it<cnt) it++;
if(!vis[it]&&a[i]+a[it]>=mid)
ans++,vis[i]=vis[it]=1;
}
b[x]=0;
for(int i=1;i<=cnt;i++)
if(vis[i]==0)
b[x]=a[i];
}
bool check(int x){
ans=0;
dfs(1,0);
return ans>=m;
}
/*!@#$%^&*!@#$%^&*~~优美的分界线~~*&^%$#@!*&^%$#@!*/
signed main(){
cin>>n>>m;
for(int i=1;i<n;i++){
int a,b,l;cin>>a>>b>>l;
add(a,b,l),add(b,a,l);
}
int l=0,r=1e17;
while(l<r){
mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<'\n';
return 0;
}
by __H_J_M__
by huangjieming0703