考场上打了一个 \(vector\) 解法,因为我当时不会 \(multiset\)

好吧,我来讲一讲今年的 \(tgD1T3\)

首先,这题 \(55\) 分是不难想的

1、 \(b_i=a_i+1\) 的情况(一条链)

解法:把所有边权记录下来,这种情况等价于将序列分割成 \(m\) 段,使 \(m\) 段区间和的最小值最大

那么二分 \(m\) 段区间和的最小值,然后 \(O(n)\) 贪心扫一遍,时间复杂度 \(O(nlogn)\)

namespace subtask1{
int a[maxn];
void dfs(int x,int fa){
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa) continue;
dfs(y,x);
a[x]=e[i].val;
}
}
int check(int k){
int t=0,now=0;
for(int i=1;i<n;i++){
if(now+a[i]>=k){
now=0;
t++;
}
else now+=a[i];
}
return t>=m;
}
void solve(){
dfs(1,0);
int l=1,r=sum,mid;
while(l<r){
mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return ;
}
}

2、 \(m=1\) 的情况(树的直径)

解法:取一条最长链,即为树的直径问题,记录一下最大值和次大值,每次把最大

值传到它的父亲,时间复杂度 \(O(n)\)

namespace subtask2{
int dfs(int x,int fa){
int sum1=0,sum2=0;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa) continue;
sum2=max(sum2,dfs(y,x)+e[i].val);
if(sum2>sum1) swap(sum1,sum2);
}
ans=max(ans,sum1+sum2);
return sum1;
}
void solve(){
dfs(1,0);
printf("%d\n",ans);
return ;
}
}

3、\(a_i=1\)的情况(菊花图)

解法:把所有边权记录下来,从大到小排序。设边权为 \(w\),答案即为 \(w_1+w_{2m-1},w_2+w_{2m-2},...,w_m+w_{m+1}\) 的最小值,时间复杂度 \(O(nlogn)\)

namespace subtask3{
int a[maxn];
bool cmp(int a,int b){
return a>b;
}
void solve(){
for(int i=head[1],y;i;i=e[i].next){
y=e[i].to;
a[y-1]=e[i].val;
}
sort(a+1,a+n,cmp);
int ans=inf;
for(int i=1;i<=m;i++)
ans=min(ans,a[i]+a[2*m-i+1]);
printf("%d\n",ans);
}
}

分支不超过 \(3\) 的话其实就是正解的弱化版

看到题意描述第一反应就是先二分那个修建的\(m\)条赛道中长度最小的赛道的长度 \(k\) ,然后 \(O(n)\) 或 \(O(nlogn)\) 判断

那么怎么判断呢?

对于每个结点,把所有传上来的值 \(val\) 放进一个 \(multiset\) ,其实这些值对答案有贡献就两种情况:

  • \(val\geq k\)
  • \(val_a+val_b\geq k\)

那么第一种情况可以不用放进 \(multiset\),直接答案 \(+1\) 就好了。第二种情况就可以对于每一个最小的元素,在 \(multiset\) 中找到第一个 \(\geq k\)的数,将两个数同时删去,最后把剩下最大的值传到那个结点的父亲

我出考场后想为什么这种解法是正确的,有没有可能对于有些情况直接传最大的数会使答案更大?

当然不会。这个数即使很大也只能对答案贡献加 \(1\),在其没传上去的时候可以跟原来结点的值配对,也只能对答案贡献加 \(1\)

\(multiset\) 版:

int dfs(int x,int fa,int k){
s[x].clear();
int val;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa) continue;
val=dfs(y,x,k)+e[i].val;
if(val>=k) ans++;
//直接处理第一种情况
else {
s[x].insert(val);
}
}
int Max=0;
while(!s[x].empty()){
if(s[x].size()==1){
return max(Max,*s[x].begin());
}
//把最大的给传上去
it=s[x].lower_bound(k-*s[x].begin());
//二分到那个值
if(it==s[x].begin()&&s[x].count(*it)==1) it++;
//若找到的就是它自己且当前值的count==1,迭代器++
if(it==s[x].end()){
Max=max(Max,*s[x].begin());
s[x].erase(s[x].find(*s[x].begin()));
}
//若没有找到比k-*s[x].begin()大的,就取个最大值,把*s[x].begin()删掉
else {
ans++;
s[x].erase(s[x].find(*it));
s[x].erase(s[x].find(*s[x].begin()));
}
//处理第二种情况
}
return Max;
//把最大值传上去
}

\(vector\) 版:

while(!s[x].empty()){
if(s[x].size()==1){
return max(Max,*s[x].begin());
}
it=lower_bound(s[x].begin(),s[x].end(),k-*s[x].begin());
if(it==s[x].begin()) it++;
if(it==s[x].end()){
Max=max(Max,*s[x].begin());
s[x].erase(s[x].begin());
}
else {
ans++;
s[x].erase(it);
s[x].erase(s[x].begin());
}
}
return Max;

\(multiset\) 版:时间复杂度 \(O(nlog^2n)\)

\(vector\) 版:时间复杂度 \(O(n^2logn)\)

备注:如果数据是随机的,\(vector\) 的写法会很快,但菊花图可以把它卡掉

然后 \(tgD1T3\) 就被我们解决了

还有就是那个二分上界可以换成树的直径

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int maxn=50000+10;
int n,m,head[maxn],tot,ans,up; struct node{
int to,next,val;
}e[maxn<<1]; multiset<int> s[maxn];
multiset<int>::iterator it; inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
} inline void add(int x,int y,int w){
e[++tot].to=y;
e[tot].val=w;
e[tot].next=head[x];
head[x]=tot;
} int dfs(int x,int fa,int k){
s[x].clear();
int val;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa) continue;
val=dfs(y,x,k)+e[i].val;
if(val>=k) ans++;
else {
s[x].insert(val);
}
}
int Max=0;
while(!s[x].empty()){
if(s[x].size()==1){
return max(Max,*s[x].begin());
}
it=s[x].lower_bound(k-*s[x].begin());
if(it==s[x].begin()&&s[x].count(*it)==1) it++;
if(it==s[x].end()){
Max=max(Max,*s[x].begin());
s[x].erase(s[x].find(*s[x].begin()));
}
else {
ans++;
s[x].erase(s[x].find(*it));
s[x].erase(s[x].find(*s[x].begin()));
}
}
return Max;
} int check(int k){
ans=0;
dfs(1,0,k);
if(ans>=m) return 1;
return 0;
} int dfs1(int x,int fa){
int sum1=0,sum2=0;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa) continue;
sum2=max(sum2,dfs1(y,x)+e[i].val);
if(sum1<sum2) swap(sum1,sum2);
}
up=max(up,sum1+sum2);
return sum1;
} int main()
{
n=read(),m=read();
int x,y,w;
for(int i=1;i<n;i++){
x=read(),y=read(),w=read();
add(x,y,w);add(y,x,w);
}
dfs1(1,0);
int l=1,r=up,mid;
while(l<r){
mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}

[NOIP2018]赛道修建(二分+multiset)的更多相关文章

  1. luogu5021 [NOIp2018]赛道修建 (二分答案+dp(贪心?))

    首先二分一下答案,就变成了找长度>=m的 不相交的路径的个数 考虑到在一个子树中,只有一个点能出这个子树去和别的点搞 所以我这个子树里尽量自我满足是不会有坏处的 而且要在自我满足数最大的条件下, ...

  2. Luogu5021 [NOIP2018]赛道修建

    Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...

  3. 【LG5021】[NOIP2018]赛道修建

    [LG5021][NOIP2018]赛道修建 题面 洛谷 题解 NOIP之前做过增强版还没做出来\(QAQ\) 一看到题目中的最大值最小,就很容易想到二分答案 重点是考虑如何\(check\) 设\( ...

  4. 竞赛题解 - NOIP2018 赛道修建

    \(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解 额--考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ 现在把坑填好了~ 题目 (Copy from 洛谷) 题 ...

  5. $Noip2018/Luogu5021$ 赛道修建 二分+树形

    $Luogu$ $Sol$ 一直以为是每个点只能经过一次没想到居然是每条边只能经过一次$....$ 首先其实这题$55$分的部分分真的很好写啊,分别是链,数的直径和菊花图,这里就不详细说了. 使得修建 ...

  6. 【比赛】NOIP2018 赛道修建

    最小值最大,二分长度 然后判断赛道大于等于这个长度最多可以有多少条 可以贪心,对于一个点和它的一些儿子,儿子与儿子之间尽量多配(排序后一大一小),剩下的选个最长的留给自己的父亲就好了 具体实现可以用一 ...

  7. [NOIP2018]赛道修建

    嘟嘟嘟 因为一些知道的人所知道的,不知道的人所不知道的原因,我来改写今年的NOIP了. 现在看这题,心中满是疑问:我当时是多么的zz,这种水题为啥没做出来-- 不管了,说正事. 先考虑部分分. 1.\ ...

  8. 【题解】NOIP2018 赛道修建

    题目戳我 \(\text{Solution:}\) 根据题目信息简化题意,是让你在树上找出\(m\)条路径使得路径长度最小值最大. 看到题第一感先二分一个答案,问题转化为如何选择一些路径使得它们最小值 ...

  9. 【题解】 P5021赛道修建

    [题解]P5021 赛道修建 二分加贪心,轻松拿省一(我没有QAQ) 题干有提示: 输出格式: 输出共一行,包含一个整数,表示长度最小的赛道长度的最大值. 注意到没,最小的最大值,还要多明显? 那么我 ...

随机推荐

  1. 栈(NOIP2003&水题测试2017082501)

    题目链接:栈 这题不难. 我们看一下,其实可以发现是卡特兰数. 不知道卡特兰数?没事,给你简单讲一下. 卡特兰数的递推式f(n)=f(0)*f(n-1)+f(1)*f(n-2)+-+f(n-2)*f( ...

  2. 绝对强大的三个linux指令: ar, nm, objdump

    前言如果普通编程不需要了解这些东西,如果想精确控制你的对象文件的格式或者你想查看一下文件对象里的内容以便作出某种判断,刚你可以看一下下面的工具:objdump, nm, ar.当然,本文不可能非常详细 ...

  3. python之函数篇3

    一:函数的定义 1)函数的简单使用,无参函数 def f1(): # 定义函数指定函数名 print("hello") # 指定功能 f1() # 调用函数,才能执行函数体里面的功 ...

  4. static与非static的区别

    static 静态的,可以修饰变量或者方法 用于变量的区别 1. static 修饰的变量称为类变量或全局变量或成员变量,在类被加载的时候成员变量即被初始化,与类关联,只要类存在,static变量就存 ...

  5. crt转cer ,6.0以上的android系统证书请求配置

    1.在服务器人员,给你发送的crt证书后,进到证书路径,执行下面语句  openssl x509 -in 你的证书.crt -out 你的证书.cer -outform der 这样你就可以得到cer ...

  6. Router types

    Inq-n. Flits are stored at the input of the router. Each input unit is connected to the switch by as ...

  7. 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...

  8. CURL模拟表单post提交及相关常用参数的使用(包括提交表单同时上传文件)

    转载自:https://blog.csdn.net/freedomwjx/article/details/43278157 (注:在curl前面加上time如time curl xxx,可以在最后显示 ...

  9. Redis集群的主从切换研究

    目录 目录 1 1. 前言 1 2. slave发起选举 2 3. master响应选举 5 4. 选举示例 5 5. 哈希槽传播方式 6 6. 一次主从切换记录1 6 6.1. 相关参数 6 6.2 ...

  10. 统计UPD丢包工具

    下载位置:https://github.com/eyjian/libmooon/tree/master/shell #!/bin/bash # 统计UPD丢包工具 # 可选参数1:统计间隔(单位:秒, ...