- https://www.luogu.org/problemnew/show/P2680
- 题意:首先这是一棵n个节点的树,然后对于树上的m条链,我们可以选取树上的唯一一条边使它的边权变为0
- 求处理后最长链的长度,要求使得最后最长链长度最小,最大值最小问题,二分答案
- 思路:二分答案肯定是二分的时间,然后关键是预处理与二分的check怎么实现。预处理可以通过LCA 求出树上任意两点
- 的距离,然后可以对m条链进行一下预处理出每条的长度,check时检验哪些边长度超过了当前枚举的答案,然后对这些
- 边进行差分更新,跑一边dfs更新答案,因为我们只对长度大于当前枚举答案的那些边进行更新差分数组,所以为了能够
- 最终达到一个合法状态,删除的边至少得满足(这些长度大于当前答案的链 都经过这条边)所以我们的更新条件为:
-
if(power[edge[i].v]==sp&&edge[i].w>ans)ans=edge[i].w;
- 求一个经过这条边的非法的链的数目为sp 并且 边值最大的删掉是最优的
-
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 301212 int n,m,a,b,t,u,v,cnt,c; int head[maxn],deep[maxn]; int dp[maxn][50],power[maxn]; ll ans,dis[maxn],L,R,mid,sp; struct node { int v,to,w; } edge[maxn]; struct edg { int x,y,z; } orz[maxn]; struct data { int u,v,fa; ll sum; bool operator<(const data&b)const { return sum>b.sum; } } lhk[maxn]; void add(int u,int v,int w) { edge[++cnt].v=v; edge[cnt].to=head[u]; edge[cnt].w=w; head[u]=cnt; } void dfs(int cur,int fa) { deep[cur]=deep[fa]+1; dp[cur][0]=fa; for(int j=1; (1<<j)<=deep[cur]; j++) dp[cur][j]=dp[dp[cur][j-1]][j-1]; for(int i=head[cur]; i!=-1; i=edge[i].to) { if(edge[i].v==fa)continue; dis[edge[i].v]=dis[cur]+edge[i].w; dfs(edge[i].v,cur); } } int lca(int x,int y) { if(deep[x]<deep[y])swap(x,y); for(int i=30; i>=0; i--) if(deep[y]+(1<<i)<=deep[x]) x=dp[x][i]; if(x==y)return x; for(int i=30; i>=0; i--) if(dp[x][i]!=dp[y][i]) { x=dp[x][i]; y=dp[y][i]; } return dp[x][0]; } void get(int cur,int fa) { for(int i=head[cur]; i!=-1; i=edge[i].to) { if(edge[i].v==fa)continue; get(edge[i].v,cur); power[cur]+=power[edge[i].v]; if(power[edge[i].v]==sp&&edge[i].w>ans)ans=edge[i].w; } } bool ok(ll cur) { memset(power,0,sizeof(power)); if(lhk[0].sum<=cur)return true; bool flag=0; sp=ans=0; for(int i=0; i<m; i++) { if(lhk[i].sum<=cur) break; power[lhk[i].u]++; power[lhk[i].v]++; power[lhk[i].fa]-=2; sp++; } get(1,0); if(lhk[0].sum-ans>cur)return 0; return 1; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1; i<n; i++) { scanf("%d%d%d",&u,&v,&c); add(u,v,c); add(v,u,c); orz[i].x=u; orz[i].y=v; orz[i].z=c; } dis[1]=deep[0]=0; dfs(1,0); for(int i=0; i<m; i++) { scanf("%d%d",&lhk[i].u,&lhk[i].v); int qyn=lca(lhk[i].u,lhk[i].v); lhk[i].sum=dis[lhk[i].u]+dis[lhk[i].v]-2*dis[qyn]; lhk[i].fa=qyn; } ans=0; sort(lhk,lhk+m); R=1e12; L=0; while(L<R) { mid=(L+R)/2; if(ok(mid))R=mid; else L=mid+1; } printf("%lld\n",L); return 0; }
P2680 运输计划 A-二分答案-树上边差分
最新推荐文章于 2018-12-19 21:38:52 发布