题面

首先要明白一个事情,虽然某次考试考场上我就已经推出来这个结论了,但很遗憾,那次考试没用上,而过了这么久,在这道题里偶然遇见,甚是惊喜。

其实也没什么,最短路跑出来的结果一定是一棵树,至少用迪杰斯特拉算法跑出来的结果肯定是一棵树。为什么?你看它的算法流程就知道了。这个算法就是尝试在已经确定的点集里挑一个,然后尝试更新其它点。换个描述方法就是在已经生成的这棵树里挑选一个叶子节点,将一些树外节点作为它的儿子加入树中(个人观点如果有错敬请原谅)。

于是就可以做这道题了。首先把最短路树建出来,做到这一点只需在跑最短路的时候记录一下由哪个节点更新到它就可以了。于是题目中的加一条奇怪边的操作就等价于把树上某个节点和根节点连起来。然后考虑对于树上的一个节点,加入把它和根节点用一条长度为 \(T\) 的边连起来,对于哪些节点的最短路会有影响。题目中说,只有一头奶牛原来的路上碰到了这条边时才会走,而在树上所有路径都相当于是从叶子向根靠拢,得出结论,只有树中以 \(i\) 为根的子树里的节点才会通过捷径到达根节点。

那么这样做,减少的路径是多少呢?明显最后需要达到的效果是所有能走捷径的牛都会走捷径,不然这条路就没有意义了。得出结论,假如在节点 \(i\) 修了捷径,那么走捷径的牛的数量就会是以它为根的子树里所有节点上的牛数之和。而每头牛走了捷径之后节省的距离都是:从根节点到 \(i\) 的距离 \(-\) 这条捷径的长度。这两个指标都是可以求的,最后我们要做的就是枚举每一个点,计算它的贡献(能走捷径的牛数乘上节省的距离),取最大值输出即可。

码风清奇,见谅。

AC记录

#include<cstdio>
#include<cstring>
#include<queue>
//#define zczc
#define int long long
using namespace std;
const int N=10010;
const int M=50010; 
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}

inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}
int m,n,cost;
namespace g{
	struct edge{
		int t,v,next;
	}e[M<<1];
	int esum,head[N];
	inline void add(int fr,int to,int val){
		esum++;
		e[esum].t=to;
		e[esum].v=val;
		e[esum].next=head[fr];
		head[fr]=esum;
		return;
	}
	int dis[N],to[N],va[N];
	bool vis[N];
	struct node{
		int wh,dis;
	};
	bool operator <(node s1,node s2){
		return s2.dis<s1.dis;
	}
	priority_queue<node>q;
	void solve(){
		int s1,s2,s3;
		for(int i=1;i<=n;i++){
			read(s1);read(s2);read(s3);
			add(s1,s2,s3);add(s2,s1,s3);
		}
		memset(dis,0x3f,sizeof(dis));
		dis[1]=0;q.push((node){1,0});
		while(!q.empty()){
			int wh=q.top().wh,nd=q.top().dis;q.pop();
			if(vis[wh])continue;vis[wh]=true;
			for(int i=head[wh],th;i;i=e[i].next){
				th=e[i].t;
				if(dis[wh]+e[i].v<dis[th]){
					dis[th]=dis[wh]+e[i].v,to[th]=wh,va[th]=e[i].v;
					q.push((node){th,dis[th]});
				}
				if(dis[wh]+e[i].v==dis[th]&&wh<to[th]){
					to[th]=wh,va[th]=e[i].v;
				}
			}
			//printf("now:%lld\n",wh);
			//for(int i=1;i<=m;i++)printf("%lld %lld\n",dis[i],to[i]);
		}
		return;
	}
}
namespace t{
	int a[N],len[N],size[N];
	struct edge{
		int t,v,next;
	}e[N<<1];
	int esum,head[N];
	inline void add(int fr,int to,int val){
		esum++;
		e[esum].t=to;
		e[esum].v=val;
		e[esum].next=head[fr];
		head[fr]=esum;
		return;
	}
	void build(int wh,int fa,int nlen){
		size[wh]=a[wh];len[wh]=nlen;
		for(int i=head[wh],th;i;i=e[i].next){
			th=e[i].t;if(th==fa)continue;
			build(th,wh,nlen+e[i].v);
			size[wh]+=size[th];
		}
		return;
	}
	void solve(){
	    for(int i=2;i<=m;i++){
			add(i,g::to[i],g::va[i]);
			add(g::to[i],i,g::va[i]);
		}
		build(1,0,0);
		int ans=0;
		for(int i=1;i<=m;i++)ans=max(ans,(len[i]-cost)*size[i]);
		printf("%lld",ans);
	}
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);read(cost);
	for(int i=1;i<=m;i++)read(t::a[i]);
	g::solve();
	t::solve();
	
	return 0;
}
一如既往,万事胜意