我与 P2680运输计划 的故事

题目链接

这是一个很长的故事。

2021年9月,我因为填错答题卡未过初赛………………

收到成绩的不同,我一直很沉痛的心理去上课OI课

我点开来这道题。

那时我图个好玩,写了一个超级暴力:

(正解是lca+二分,但是我上全源最短路,直接十分的玄学)

#include<cstdio>
#include<queue>
#include<algorithm>
#define N 500005
#define int long long
#define clear(arr,b) for(int i=1;i<N;i++)arr[i]=b
using namespace std;
int head[N],to[N],nxt[N],val[N],n,m,s,dis[N],tot;
bool vis[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
void dijkstra(int s){
	priority_queue<pair<int ,int > >q;
	clear(dis,2147483647);
	clear(vis,0);
	dis[s]=0;
	q.push(make_pair(0,s));
	while(!q.empty()){
		int x=q.top().second;q.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i],w=val[i];
			if(dis[y]>dis[x]+w){
				dis[y]=dis[x]+w;
				if(!vis[y])q.push(make_pair(-dis[y],y));
			}
		}
	}
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	int ans=0x7ffff;
	for(int i=1;i<=m;i++){
		int a=0,b=0;scanf("%lld%lld",&a,&b);
		dijkstra(a);
		ans=min(dis[b],ans);
	}
	printf("%lld\n",ans);
	return 0;
}

最终我成功骗到了五分。

好像我怎么消沉了,我几乎没碰。

这道题石沉大海,没有翻出来过了。

半年过去了,我因为突发疫情在家上网课。

从此开始自学OI。

从tarjan缩点一直学到Splay。

没有停止来的英文,开学了,马上要中考了。

我沉迷OI,在学校模拟考也不算太差(是可以直升的档次),也这种情况还在意。

中考还是来了,什么都希望,去拼了一把,落但榜了。

去了一个不那么好的学校,阴差阳错的又点看这道题。

于是我又开始写了。

#include<cstdio>
#include<algorithm>
#define N 600006
using namespace std;
int tot,n,m,head[N],to[N],nxt[N],val[N];
int dep[N],f[N][34],g[N][34],lg[N],dis[N],l,r,v[N];
struct node{
	int u,v,lca,mx,dis;
}a[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
inline void csh(){for(int i=1;i<=n;i++)lg[i]=lg[i>>1]+1;}
void rep(int x,int fa){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=lg[dep[x]];i++){
		f[x][i]=f[f[x][i-1]][i-1];
		g[x][i]=max(g[x][i],g[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa)continue;
		g[y][0]=val[i];
		v[y]=val[i];
		dis[y]=dis[x]+val[i];
		rep(y,x);
	}
}
pair<int,int> lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int res=0;
	while(dep[x]!=dep[y]){
		int i=lg[dep[x]-dep[y]];
		res=max(res,g[x][i]);
		x=f[x][i];
	}
	if(x==y)return {res,x};
	for(int i=lg[dep[x]];i>=0;i--){
		if(f[x][i]!=f[y][i]){
			res=max(res,g[x][i]);
			res=max(res,g[y][i]);
			x=f[x][i];y=f[y][i];
		}
	}
	return {res,f[x][0]};
}
bool check(int mid){
	int mx=0;
	for(int i=1;i<=m;i++)mx=max(mx,a[i].dis-a[i].mx);
	if(mx>mid)return 0;
	else return 1;
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
		r+=c;
	}
	rep(1,0);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a[i].u,&a[i].v);
		pair<int,int> ans=lca(a[i].u,a[i].v);
		a[i].mx=ans.first;
		a[i].lca=ans.second;
		a[i].dis=dis[a[i].u]+dis[a[i].v]-2*dis[a[i].lca];
	}
	while(l<r){
		int mid=(l+r)/2;
		if(!check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%d",l);
	return 0;
}

这个代码可以得到45分,但是我的lg甚至都没有初始化,如果我打2015年NOIP,那么我就可以白捡45分。

这不是在考场,我继续思考。

于是我把初始化添加了:

#include<cstdio>
#include<algorithm>
#define N 600006
using namespace std;
int tot,n,m,head[N],to[N],nxt[N],val[N];
int dep[N],f[N][34],g[N][34],lg[N],dis[N],l,r,v[N];
struct node{
	int u,v,lca,mx,dis;
}a[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
inline void csh(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void rep(int x,int fa){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=lg[dep[x]];i++){
		f[x][i]=f[f[x][i-1]][i-1];
		g[x][i]=max(g[x][i],g[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa)continue;
		g[y][0]=val[i];
		v[y]=val[i];
		dis[y]=dis[x]+val[i];
		rep(y,x);
	}
}
pair<int,int> lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int res=0;
	while(dep[x]!=dep[y]){
		int i=lg[dep[x]-dep[y]];
		res=max(res,g[x][i]);
		x=f[x][i];
	}
	if(x==y)return {res,x};
	for(int i=lg[dep[x]];i>=0;i--){
		if(f[x][i]!=f[y][i]){
			res=max(res,g[x][i]);
			res=max(res,g[y][i]);
			x=f[x][i];y=f[y][i];
		}
	}
	return {res,f[x][0]};
}
bool check(int mid){
	int mx=0;
	for(int i=1;i<=m;i++)mx=max(mx,a[i].dis-a[i].mx);
	if(mx>mid)return 0;
	else return 1;
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
		r+=c;
	}
	csh();
	rep(1,0);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a[i].u,&a[i].v);
		pair<int,int> ans=lca(a[i].u,a[i].v);
		a[i].mx=ans.first;
		a[i].lca=ans.second;
		a[i].dis=dis[a[i].u]+dis[a[i].v]-2*dis[a[i].lca];
	}
	while(l<r){
		int mid=(l+r)/2;
		if(!check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%d",l);
	return 0;
}

搞笑是,只能得15分了。

我重新构建了检查函数,这时我仍然需要 15 分钟,但我很生气。

#include<cstdio>
#include<algorithm>
#define N 600006
#define int long long
using namespace std;
int tot,n,m,head[N],to[N],nxt[N],val[N];
int dep[N],f[N][34],g[N][34],lg[N],dis[N],l,r;
struct node{
	int u,v,lca,mx,dis;
}a[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
inline void csh(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void rep(int x,int fa){
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=lg[dep[x]];i++){
		f[x][i]=f[f[x][i-1]][i-1];
		g[x][i]=max(g[x][i],g[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa)continue;
		g[y][0]=val[i];
		dis[y]=dis[x]+val[i];
		rep(y,x);
	}
}
pair<int,int> lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int res=0;
	while(dep[x]!=dep[y]){
		int i=lg[dep[x]-dep[y]];
		res=max(res,g[x][i]);
		x=f[x][i];
	}
	if(x==y)return {res,x};
	for(int i=lg[dep[x]];i>=0;i--){
		if(f[x][i]!=f[y][i]){
			res=max(res,g[x][i]);
			res=max(res,g[y][i]);
			x=f[x][i];y=f[y][i];
		}
	}
	return {res,f[x][0]};
}
bool check(int mid){
	int mx=0,sec=0,p=0;
	for(int i=1;i<=m;i++){
		if(a[i].dis>sec&&a[i].dis<mx)sec=mx;
		if(a[i].dis>mx)sec=mx,mx=a[i].dis,p=i;
		if(a[i].dis==mx&&a[i].mx>a[p].mx)sec=mx,mx=a[i].dis,p=i;
	}
	mx=max(sec,mx-a[p].mx);
	if(mx>mid)return 0;
	else return 1;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
		r+=c;
	}
	csh();
	rep(1,0);
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i].u,&a[i].v);
		pair<int,int> ans=lca(a[i].u,a[i].v);
		a[i].mx=ans.first;
		a[i].lca=ans.second;
		a[i].dis=dis[a[i].u]+dis[a[i].v]-2*dis[a[i].lca];
		//printf("%d %d %d\n",a[i].mx,a[i].lca,a[i].dis);
	}
	while(l<r){
		int mid=(l+r)/2;
		if(!check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%lld",l);
	return 0;
}

我仔细考虑先上链,再看一眼和,最后一个链被查到的次数,当一个点被统计出的次数大于链中的情况时,表示链中总有超过链中的还有数时,说明再判断一下这个点到他父亲的距离就行了,如果这个距离之后,那么检查就成立了。

bool check(int mid){
	int cnt=0,del=0;
	for(int i=1;i<=n;i++)tmp[i]=0;
	for(int i=1;i<=m;i++){
		if(a[i].dis>mid){
			tmp[a[i].u]++,tmp[a[i].v]++,tmp[a[i].lca]-=2;
			del=max(del,a[i].dis-mid);
			cnt++;
		}
	}
	if(!cnt)return 1;
	for(int i=n;i>=1;i--)tmp[f[dfn[i]][0]]+=tmp[dfn[i]];
	for(int i=2;i<=n;i++)if(tmp[i]==cnt&&dis[i]-dis[f[i][0]]>=del)return 1;
	return 0;
}

最终,这样,可以自学长一下,其实就是检查树上的所有操作。

最后我成功AC了本题

#include<cstdio>
#include<algorithm>
#define N 600006
#define int long long
using namespace std;
int tot,n,m,head[N],to[N],nxt[N],val[N];
int dep[N],f[N][34],g[N][34],lg[N],dis[N],l,r,dfn[N],cnt;
int tmp[N];
struct node{
	int u,v,lca,mx,dis;
}a[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
inline void csh(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void rep(int x,int fa){
	dfn[++cnt]=x;
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=lg[dep[x]];i++){
		f[x][i]=f[f[x][i-1]][i-1];
		g[x][i]=max(g[x][i],g[f[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa)continue;
		g[y][0]=val[i];
		dis[y]=dis[x]+val[i];
		rep(y,x);
	}
}
pair<int,int> lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int res=0;
	while(dep[x]!=dep[y]){
		int i=lg[dep[x]-dep[y]];
		res=max(res,g[x][i]);
		x=f[x][i];
	}
	if(x==y)return {res,x};
	for(int i=lg[dep[x]];i>=0;i--){
		if(f[x][i]!=f[y][i]){
			res=max(res,g[x][i]);
			res=max(res,g[y][i]);
			x=f[x][i];y=f[y][i];
		}
	}
	return {res,f[x][0]};
}
bool check(int mid){
	int cnt=0,del=0;
	for(int i=1;i<=n;i++)tmp[i]=0;
	for(int i=1;i<=m;i++){
		if(a[i].dis>mid){
			tmp[a[i].u]++,tmp[a[i].v]++,tmp[a[i].lca]-=2;
			del=max(del,a[i].dis-mid);
			cnt++;
		}
	}
	if(cnt==0)return 1;
	for(int i=n;i>=1;i--)tmp[f[dfn[i]][0]]+=tmp[dfn[i]];
	for(int i=2;i<=n;i++)if(tmp[i]==cnt&&dis[i]-dis[f[i][0]]>=del)return 1;
	return 0;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
		r+=c;
	}
	csh();
	rep(1,0);
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i].u,&a[i].v);
		pair<int,int> ans=lca(a[i].u,a[i].v);
		a[i].mx=ans.first;
		a[i].lca=ans.second;
		a[i].dis=dis[a[i].u]+dis[a[i].v]-2*dis[a[i].lca];
		//printf("%d %d %d\n",a[i].mx,a[i].lca,a[i].dis);
	}
	while(l<r){
		int mid=(l+r)/2;
		if(!check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%lld",l);
	return 0;
}

发现没有一下,所以拿掉,顺便整理一下:

#include<cstdio>
#include<algorithm>
#define N 600006
#define int long long
using namespace std;
int tot,n,m,head[N],to[N],nxt[N],val[N];
int dep[N],f[N][34],lg[N],dis[N],l,r,dfn[N],cnt;
int tmp[N];
struct node{
	int u,v,lca,mx,dis;
}a[N];
void add(int u,int v,int w){
	to[++tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	val[tot]=w;
}
inline void csh(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void rep(int x,int fa){
	dfn[++cnt]=x;
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(int i=1;i<=lg[dep[x]];i++)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa)continue;
		dis[y]=dis[x]+val[i];
		rep(y,x);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	while(dep[x]!=dep[y]){
		int i=lg[dep[x]-dep[y]];
		x=f[x][i];
	}
	if(x==y)return x;
	for(int i=lg[dep[x]];i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];	
	return f[x][0];
}
bool check(int mid){
	int cnt=0,del=0;
	for(int i=1;i<=n;i++)tmp[i]=0;
	for(int i=1;i<=m;i++){
		if(a[i].dis>mid){
			tmp[a[i].u]++,tmp[a[i].v]++,tmp[a[i].lca]-=2;
			del=max(del,a[i].dis-mid);
			cnt++;
		}
	}
	if(cnt==0)return 1;
	for(int i=n;i>=1;i--)tmp[f[dfn[i]][0]]+=tmp[dfn[i]];
	for(int i=2;i<=n;i++)if(tmp[i]==cnt&&dis[i]-dis[f[i][0]]>=del)return 1;
	return 0;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;i++){
		int a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
		r+=c;
	}
	csh();
	rep(1,0);
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i].u,&a[i].v);
		int ans=lca(a[i].u,a[i].v);
		a[i].lca=ans;
		a[i].dis=dis[a[i].u]+dis[a[i].v]-2*dis[ans];
	}
	while(l<r){
		int mid=(l+r)/2;
		if(!check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%lld",l);
	return 0;
}

很好,作为SFLS之前最惨的OIer,希望天下没有OIer和我一样惨。

结完撒花吧,虽然挺难受的。

posted @ 2022-08-22 15:45  灵长同志  阅读(25)  评论(0)    收藏  举报