运输计划
- 关键字:树链剖分,二分,差分
结果和评价
- 得分:70
- 时间:
- 评价:不应该
我的思路
- 暴力直接60
- 直接否定了二分,然后瞎搞,多水了10,没有什么思考。。。
正确思路
- 求最长路径最短,应该要先考虑考虑二分。
- 二分一个答案之后,比答案小的
rout
都无需考虑了
- 然后我们考虑一下要删什么边,显然要删边一定在到剩下 rout 的交集里。
- 而且一定要满足可以从中取出一条边满足 Mxrout−Mxedge<=limt
讲道理这个思路是非常顺的。
怎么说呢,可以思考一下只有一条边的情况,我们要删的边一定是路径上最长的边。
也就是说,我们要删的边是使最后路径全部合法的边。是可以确定的。
- 然后我们发现现在有个问题需要解决
- 高效得覆盖路径
- 高效查询出所以路径交集中的最大边
- 怎么覆盖呢。。。
- 先看看链的情况。对于一个路径 s,t ,我们可以用一个 cov 数组记录每个点被覆盖了几次。
- 然后把 cov[s+1,t] (我们用标记子节点的方式标记边)都加1。。。复杂度为 O(n∗m) 的,没有什么屁用。
- 发现了吧,完全可以用差分数组来实现这一覆盖的过程,复杂度为 O(n+m)
- 这样就
轻松地拿到了80分 - 然后考虑一下树的情况。。。
- 额,树上的路径覆盖。。。链上的路径覆盖。。。好像树剖一下就没有什么区别了。。。
- 每次,直接树剖一下,就可以了。
- 复杂度为 (log(n∗t)∗(log(n)∗m+n)
- 由于树剖的常数是比较优的,所以可以轻松跑过。。
实现
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<time.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<string>
using namespace std;
#define bug(x) cerr<<#x<<'='<<x<<' '
#define debug(x) cerr<<#x<<'='<<x<<'\n'
#define For(i,a,b) for(register int i=a;i<=b;++i)
#define Ror(i,a,b) for(register int i=b;i>=a;--i)
typedef long long ll;
template<class T>void rd(T&x){
x=0;char c,f=1;
while(c=getchar(),!isdigit(c))if(c=='-')f=-1;
do x=(x<<1)+(x<<3)+(c^'0');
while(c=getchar(),isdigit(c));
x*=f;
}
template<class T>void pf(T x){
static int top=0,stk[1000];
if(!x)putchar('0');
if(x<0)putchar('-'),x=-x;
while(x)stk[++top]=x%10,x/=10;
while(top)putchar(stk[top--]+'0');
}
template<class T>void pt(T x){pf(x),putchar(' ');}
template<class T>void ptn(T x){pf(x),putchar('\n');}
template<class T>void Max(T&x,T y){if(x<y)x=y;}
template<class T>void Min(T&x,T y){if(y<x)x=y;}
const int M=3e5+5,INF=1e9;
int fa[M],tp[M],son[M],sz[M],dep[M],n,m,dis[M],to[M];
struct node{int s,t,v;}rout[M];
struct E{int t,id,nxt;}G[M<<1];
int h[M],E_tot,cost[M],mp[M],Dfn,dfn[M];
void E_Add(int x,int y,int id){
G[++E_tot]=(E){x,id,h[y]};h[y]=E_tot;
G[++E_tot]=(E){y,id,h[x]};h[x]=E_tot;
}
void dfs(int x,int f,int d){
dis[x]=d;
fa[x]=f;sz[x]=1;
dep[x]=dep[f]+1;
for(int i=h[x];i;i=G[i].nxt){
int y=G[i].t,c=cost[G[i].id];
if(y==f)continue;
to[y]=G[i].id;
dfs(y,x,d+c);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y])son[x]=y;
}
}
void rfs(int x){
mp[dfn[x]=++Dfn]=x;
if(son[x]){
tp[son[x]]=tp[x];
rfs(son[x]);
}
for(int i=h[x];i;i=G[i].nxt){
int y=G[i].t;
if(y==fa[x]||y==son[x])continue;
rfs(tp[y]=y);
}
}
void init(){
dfs(1,0,0);
rfs(tp[1]=1);
}
int LCA(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
x=fa[tp[x]];
}
if(dep[y]<dep[x])return y;
return x;
}
int calc(node x){
int s=x.s,t=x.t,v=LCA(s,t);
return dis[s]+dis[t]-2*dis[v];
}
int Add[M];
void insert(int l,int r){
++Add[l],--Add[r+1];
}
void EAdd(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
int l=dfn[tp[x]],r=dfn[x];
insert(l,r);
x=fa[tp[x]];
}
if(x!=y){
int l=dfn[x],r=dfn[y];
if(l>r)swap(l,r);
insert(l+1,r);
}
}
bool check(int limt){
int cnt=0,Mxr=0,Mxe=0;
memset(Add,0,sizeof(Add));
For(i,1,m)if(limt<rout[i].v){
++cnt;
EAdd(rout[i].s,rout[i].t);
if(Mxr<rout[i].v)Mxr=rout[i].v;
}
int cov=0;
For(i,1,n){
cov+=Add[i];
if(cov==cnt){
int d=cost[to[mp[i]]];
if(Mxe<d)Mxe=d;
}
}
if(Mxr-Mxe<=limt)return 1;
return 0;
}
int main(){
rd(n),rd(m);
For(i,1,n-1){
int a,b;
rd(a),rd(b),rd(cost[i]);
E_Add(a,b,i);
}
init();
For(i,1,m){
rd(rout[i].s);
rd(rout[i].t);
rout[i].v=calc(rout[i]);
}
int l=0,r=3e8,res=r;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))res=mid,r=mid-1;
else l=mid+1;
}
ptn(res);
return 0;
}