[NOIP-2015] 运输计划
[NOIP-2015] 运输计划
此题做法:二分答案+LCA+树链剖分/树上差分。
我们很容易想到二分答案,毕竟答案满足单调性且较难直接计算。那么我们需要的就是一个 check
函数。我们的重点就在 check
函数上。
我们现在已知我们二分出的答案 \(x\)。我们要判断是否可行。首先,我们先把所有运输计划的两点之间的距离大于 \(x\) 的找出来,时间复杂度 \(O(m)\)。然后我们需要对这些找出来的边上经过的点标记一下。如果有 \(num\) 个询问的长度大于 \(x\),我们再找到被标记次数为 \(num\) 的所有边即可。为什么这样做?我们这是为了找出最优的虫洞,能让这些边减小量最大。因为这 \(num\) 条边的长度都超过了 \(x\)。所以我们班一定要找出这 \(num\) 个询问都经过的边才行,找出最大值,和 \(num\) 条边中长度最大的比较一下即可。思路十分清晰,我们只需要用边差分的方式优化一下即可,复杂度为 \(O(m)\)。或者也可以用树链剖分来做到 \(O(m\log n)\)。
可谁曾想,这个铁 \(O(n\log m)\) 的算法居然只有 \(95\) 分 😦
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#pragma GCC optimize("Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=3e5+10;
int n,cnt,f[maxn][23],m,h[maxn],dep[maxn],dis[maxn],maxx,sum,c[maxn],sont[maxn];
struct edge {
int v,w,next;
}e[maxn<<1];
struct querys {
int x,y,dist;
}q[maxn];
void addedge(int u,int v,int w) {
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=h[u];
h[u]=cnt;
}
void insert(int u,int v,int w) {
addedge(u,v,w);
addedge(v,u,w);
}
void dfs(int u,int fa) {
f[u][0]=fa;
dep[u]=dep[fa]+1;
for(int i=h[u];i;i=e[i].next) {
int v=e[i].v;
if(v!=fa) {
dis[v]=dis[u]+e[i].w;
dfs(v,u);
}
}
}
int LCA(int x,int y) {
if(dep[x]<dep[y]) {
swap(x,y);
}
for(int i=22;i>=0;i--) {
if(dep[f[x][i]]>=dep[y]) {
x=f[x][i];
}
}
if(x==y) {
return x;
}
for(int i=22;i>=0;i--) {
if(f[x][i]!=f[y][i]) {
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int dis_calc(int x,int y) {
return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
int num;
void get_pre(int u,int fa,int val) {
sont[u]+=c[u];
for(int i=h[u];i;i=e[i].next) {
int v=e[i].v;
if(v!=fa) {
get_pre(v,u,e[i].w);
sont[u]+=sont[v];
}
}
if(sont[u]==num) {
maxx=max(maxx,val);
}
}
bool check(int x) {
fill(sont,sont+n+1,0);
fill(c,c+n+1,0);
maxx=num=0;
int need=0;
for(int i=1;i<=m;i++) {
if(q[i].dist>x) {
num++;
need=max(need,q[i].dist-x);
c[LCA(q[i].x,q[i].y)]-=2;
c[q[i].x]++;
c[q[i].y]++;
}
}
get_pre(1,0,0);
if(need<=maxx) {
return 1;
}
else {
return 0;
}
}
signed main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read();m=read();
for(int i=1;i<n;i++) {
int a=read(),b=read(),c=read();
insert(a,b,c);
}
dfs(1,0);
for(int i=1;i<=22;i++) {
for(int j=1;j<=n;j++) {
f[j][i]=f[f[j][i-1]][i-1];
}
}
for(int i=1;i<=m;i++) {
q[i].x=read();
q[i].y=read();
q[i].dist=dis_calc(q[i].x,q[i].y);
sum+=q[i].dist;
}
int l=0,r=sum;
while(l+1<r) {
int mid=(l+r)>>1;
if(check(mid)) {
r=mid;
}
else {
l=mid;
}
}
if(check(l)) {
cout<<l<<endl;
}
else {
cout<<r<<endl;
}
//fclose(stdin);
//fclose(stdout);
return 0;
}