[USACO20FEB]Delegation P 题解
题意: \(n\) 个节点的树,边权为1,你需要求出将这颗树拆分成若干条链时链的长度最小值的最大值。
和 赛道修建 类似,都是二分一个 \(mid\), 然后能在当前子树内成链则成链,否则向上延伸,只不过这道题需要满足剩下的链的数量不能超过1。还有,如果当前点的子节点即连接的链是偶数个,那就加入一个0,使得他能至少向上贡献一条链。
坑点: (!x&1)和 (!(x&1)) 的判定结果是不同的 ,因为这个调了半个小时,mdzz。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
int ret=0, f=1; char x=getchar();
while(!isdigit(x)) { if(x=='-') f=-1; x=getchar(); }
while(isdigit(x)) { ret=ret*10+x-'0'; x=getchar(); }
return ret*f;
}
const int maxn=1e5+3;
vector <int> vec[maxn];
int head[maxn],nxt[maxn<<1],to[maxn<<1], cnt;
void add_edge(int x,int y)
{
cnt++; to[cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;
}
int d[maxn],lst[maxn];
bool work(int p,int x,int limt)
{
for(int i=0,j=(signed)vec[x].size()-1;i<j;i++,j--)
{
if(i==p) i++;
if(j==p) j--;
if(vec[x][i]+vec[x][j]<limt) { return 0; }
}
return 1;
}
int flg;
void dfs(int x,int fa,int limt)
{
vec[x].clear();
for(int i=head[x];i;i=nxt[i])
{
int v=to[i]; if(v==fa) continue;
dfs(v,x,limt);
vec[x].push_back(lst[v]+1);
}
int siz=vec[x].size();
if(!fa) { if(siz&1) { vec[x].push_back(0); siz++; } }
else { if(!(siz&1)) { vec[x].push_back(0); siz++; } }
sort(vec[x].begin(), vec[x].end());
if(!fa)
{
for(int i=0;i<siz/2;i++)
{
if(vec[x][i]+vec[x][siz-i-1]<limt) { flg=0; return; }
}
}
else
{
if(!work(0,x,limt)) { flg=0; return; }
else
{
int l=0,r=siz-1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(work(mid,x,limt)) { l=mid; }
else { r=mid-1; }
}
lst[x]=vec[x][l];
}
}
}
void check(int x)
{
flg=1;
dfs(1,0,x);
}
signed main()
{
int n=read();
for(int i=1;i<n;i++) { int x=read(), y=read(); d[x]+=1; d[y]+=1; add_edge(x,y); add_edge(y,x); }
int l=1,r=n-1;
while(l < r)
{
int mid=(l+r+1)>>1; check(mid);
cerr << l << " " << r << " " << mid << " " << flg << endl ;
if(flg) { l=mid; } else { r=mid-1; }
}
check(l); cerr << flg << endl; int ans= flg ? l : 0 ;
cout << ans << endl ;
return 0;
}
浙公网安备 33010602011771号