二分答案,贪心
(打完了LCA的板子才发现自己不会这么做)
形如:
求最小值的最大值
求最大值的最小值
这样的东西,十有八九就是二分答案了。
在这个情景下我们二分的是当前赛道允许的最短长度\(L\)。我们将检查,在这个最短长度\(L\)的限制下,能否构造大于等于m条赛道。如果能就让\(L\)大一点,如果不能就小一点。
那么现在考虑,在有当前赛道允许的最短长度\(L\)的限制下最多能开几条赛道。
定义:\(f[u]\)是\(u\)节点能向父节点方向贡献的最大赛道长度。\(cnt\)是当前已经构造的赛道数量,初始值为\(0\)。用\(w(u,v)\)表示\(u\)到\(v\)的路径长。
我们拿到一张图,考虑一个局部的形状:“扫帚”,扫帚的柄是\(u\)到父节点\(fa\)的路径,扫帚的每一根须表示\(u\)到子节点\(v_i\)的一条路径,每一根扫帚须的顶端粘着一个小垃圾\(v_i\)。我们假设在当前情形下,所有的\(f[v_i]\)都已经计算完毕。
首先将扫帚须按照\(f[v_i]+w(u,v_i)\)的升序排列,考虑这些扫帚须\(^*\);
- 如果某根扫帚须的长度\(w(u,v_i)\)加上子节点的最大贡献\(f[v_i]\)已经大于等于\(L\)了,那就说明我们得到了一个合法的赛道,
cnt++
,并将这个扫帚须从扫把上拔掉。 - 如果当前最短的那根扫帚须能找到另一个扫帚须,使得这两根扫帚须刚刚好比\(L\)大或者刚刚好等于\(L\),那就把这两根扫帚须都拔了,这样我们就得到了一个合法的赛道,
cnt++
。注意,这里的刚刚好,是严格的刚刚好,即,当前组合状态下,不能存在另一个组合使得后者的长度既大于等于\(L\)又比当前组合的长度小。 - 如果当前的扫帚须太短了,以至于上面两个条件都不满足,那就考虑这根扫帚须可能要跟父节点以上的路径进行组合才能得到一条新的赛道,并且能够获此殊荣的扫帚须是唯一的,因为所有的路径只能用一次。那么,可以列出状态转移方程:
\[ f[u]=max(f[v_i]+w(u,v_i)), \quad v_i \in Son(u) \]
这样一个扫帚就考虑完全了。
现在考虑全图,那么我们就\(dfs\)地考虑扫帚们,其中边界情况:叶子节点的初始值显然:\(f[leaf]=0\)。
然后比较一下cnt与m的大小关系,就能知道\(L\)的值应该如何调整。
还没结束。考虑这些扫帚须\(^*\)那一步涉及一个顺序的问题,我们使用multiset
进行优化,这样我们就得到了一些可爱的\(log\)。
复杂度掰了掰手指头发现不会算……估计是\(O(n \log^2 n)\)左右?
code:
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
char c=getchar();int x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x;
}
const int N=50005,INF=0x3f3f3f3f;
int n,m,cnt,curl;
int f[N];
struct node
{
int v,w;
node(){}
node(int to,int weight)
{
v=to;
w=weight;
}
};
vector<node> G[N];
void dfs(int u,int fa)
{
multiset<int> S;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
if(v!=fa)
{
dfs(v,u);
if(f[v]+w>=curl)cnt++;
else S.insert(f[v]+w);
}
}
while(!S.empty())
{
multiset<int>::iterator it_cur=S.begin();
S.erase(it_cur);
multiset<int>::iterator it=S.lower_bound(curl-*it_cur);
if(it!=S.end())cnt++,S.erase(it);
else f[u]=max(f[u],*it_cur);
}
}
inline bool chk(int x)
{
memset(f,0,sizeof(f));
cnt=0;
curl=x;
dfs(1,0);
if(cnt>=m)return 1;
else return 0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u=read();
int v=read();
int w=read();
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
int l=0,r=INF,mid,ans=0;
while(l<=r)
{
mid=((l+r)>>1);
if(chk(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
char c=getchar();int x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x;
}
const int N=50005,INF=0x3f3f3f3f;
int n,m,cnt,curl;
int f[N];
struct node
{
int v,w;
node(){}
node(int to,int weight)
{
v=to;
w=weight;
}
};
vector<node> G[N];
void dfs(int u,int fa)
{
multiset<int> S;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
if(v!=fa)
{
dfs(v,u);
if(f[v]+w>=curl)cnt++;
else S.insert(f[v]+w);
}
}
while(!S.empty())
{
multiset<int>::iterator it_cur=S.begin();
S.erase(it_cur);
multiset<int>::iterator it=S.lower_bound(curl-*it_cur);
if(it!=S.end())cnt++,S.erase(it);
else f[u]=max(f[u],*it_cur);
}
}
inline bool chk(int x)
{
memset(f,0,sizeof(f));
cnt=0;
curl=x;
dfs(1,0);
if(cnt>=m)return 1;
else return 0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u=read();
int v=read();
int w=read();
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
int l=0,r=INF,mid,ans=0;
while(l<=r)
{
mid=((l+r)>>1);
if(chk(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}
------------恢复内容结束------------
LGOJ P5021 赛道修建
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
char c=getchar();int x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x;
}
const int N=50005,INF=0x3f3f3f3f;
int n,m,cnt,curl;
int f[N];
struct node
{
int v,w;
node(){}
node(int to,int weight)
{
v=to;
w=weight;
}
};
vector<node> G[N];
void dfs(int u,int fa)
{
multiset<int> S;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].v,w=G[u][i].w;
if(v!=fa)
{
dfs(v,u);
if(f[v]+w>=curl)cnt++;
else S.insert(f[v]+w);
}
}
while(!S.empty())
{
multiset<int>::iterator it_cur=S.begin();
S.erase(it_cur);
multiset<int>::iterator it=S.lower_bound(curl-*it_cur);
if(it!=S.end())cnt++,S.erase(it);
else f[u]=max(f[u],*it_cur);
}
}
inline bool chk(int x)
{
memset(f,0,sizeof(f));
cnt=0;
curl=x;
dfs(1,0);
if(cnt>=m)return 1;
else return 0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u=read();
int v=read();
int w=read();
G[u].push_back(node(v,w));
G[v].push_back(node(u,w));
}
int l=0,r=INF,mid,ans=0;
while(l<=r)
{
mid=((l+r)>>1);
if(chk(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans;
return 0;
}