[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;
}
posted @ 2020-09-01 22:06  huayucaiji  阅读(194)  评论(0编辑  收藏  举报