【NOI P模拟赛】欢乐豆(最短路,线段树)

题面

在这里插入图片描述

样例输入

5 5
2 6 5 1 7
1 3 4
2 4 3
3 5 4
4 1 2
5 2 3

样例输出

72

数据范围

1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 3000 , 0 ≤ a i ≤ 1 0 6 . 1\leq n\leq10^5,1\leq m\leq 3000,0\leq a_i\leq10^6. 1n105,1m3000,0ai106.

题解

原图相当于每个点有个固定的代价可以跳到其他任一点,所以原图上的点,以及修改后的与修改边无关的点 u , v u,v u,v 的最短路就是 a u a_u au

所以,除了修改边覆盖的点,其他的点的贡献都是 a i ( n − 1 ) a_i(n-1) ai(n1)

被修改边覆盖的点只有最多 2 m 2m 2m 个,我们可以单独算这些点的贡献,暂时称之为特殊点。

对于一个特殊点 i i i 和所有非特殊点 j j j d i s ( i , j ) dis(i,j) dis(i,j) 一定是个定值,因为所有非特殊点对于某个特殊点是等价的——不难发现它们一定是经过一段修改路径,再“跳”到目标的,通过跳一定可以到达所有非特殊点。

所以我们需要讨论的最短路也从 2 m × n 2m\times n 2m×n 对变成了 2 m × 2 m 2m\times 2m 2m×2m 对,难点在于计算特殊点之间的最短路。

特殊点之间的修改边有 m m m 条,而“跳”边覆盖了连续的区间,因此,不难想到用线段树优化 Dijkstra (不是优化建图,是直接优化松弛)。枚举每个起点做单源最短路。

时间复杂度 O ( m 2 log ⁡ m ) O(m^2\log m) O(m2logm) ,需要卡常和剪枝。

CODE

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar xchar
LL read() {
    LL f=1,x=0;int s = getchar(); 
    while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
    while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
    return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
    if(!x) {putchar('0');return ;}
    if(x<0) {putchar('-');x = -x;}
    return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int Max(int a,int b) {return a>b ? a:b;}
int Min(int a,int b) {return a<b ? a:b;}
int a[MAXN];
map<int,int> G[MAXN];
vector<pair<int,int> > g[MAXN];
bool f[MAXN];
int b[MAXN],cn,rk[MAXN];
int dp[MAXN];
struct it{
	int nm,id;
	it(){nm=id=0;}
	it(int N,int I) {nm=N;id=I;}
}tre[MAXN];
int lz[MAXN],M;
void MN(it &y,int mn) {
	y.nm = Min(y.nm,mn);
}
it merg(it a,it b) {
	if(!a.id) return b;
	if(!b.id) return a;
	return a.nm < b.nm ? a:b;
}
void maketree(int n,int nb) {
	M=1;while(M<n+2)M<<=1;
	for(int i = 1;i < (M<<1);i ++) lz[i] = 0x3f3f3f3f,tre[i] = it();
	for(int i = 1;i <= n;i ++) tre[M+i].nm = nb,tre[M+i].id = i;
	for(int i = M-1;i > 0;i --) tre[i] = merg(tre[i<<1],tre[i<<1|1]);
}
void addtree(int x,it y) {
	int s=M+x; tre[s] = y; s >>= 1;
	while(s) MN(tre[s] = merg(tre[s<<1],tre[s<<1|1]),lz[s]),s >>= 1;
}
void addsg(int l,int r,int y) {
	if(l > r) return ;
	for(int s=M+l-1,t=M+r+1;s||t;s>>=1,t>>=1) {
		if(s<M) MN(tre[s] = merg(tre[s<<1],tre[s<<1|1]),lz[s]);
		if(t<M) MN(tre[t] = merg(tre[t<<1],tre[t<<1|1]),lz[t]);
		if((s>>1) ^ (t>>1)) {
			if(!(s&1)) MN(tre[s^1],y),lz[s^1] = Min(lz[s^1],y);
			if(t & 1) MN(tre[t^1],y),lz[t^1] = Min(lz[t^1],y);
		}
	}return ;
}
int main() {
	freopen("happybean.in","r",stdin);
	freopen("happybean.out","w",stdout);
	n = read();m = read();
	LL ans = 0;
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
		ans += a[i]*1ll*(n-1);
	}
	for(int i = 1;i <= m;i ++) {
		s = read();o = read();k = read();
		G[s][o] = k;
		f[s] = 1;f[o] = 1;
	}
	int mn = 0x3f3f3f3f;
	for(int i = 1;i <= n;i ++) {
		if(!f[i]) {
			mn = min(mn,a[i]);
		}
		else {
			ans -= a[i]*1ll*(n-1);
			b[++ cn] = i;rk[i] = cn;
			for(auto j = G[i].begin();j != G[i].end();j ++) {
				g[cn].push_back(*j);
			}
		}
	}
	for(int i = 1;i <= cn;i ++) {
		int me = a[b[i]];
		if(g[i].empty()) {
			ans += a[b[i]]*1ll*(n-1);
			continue;
		}
		maketree(cn,Min(0x3f3f3f3f,a[b[i]]+mn));
		addtree(i,it(0,i));
		for(int ii = 1;ii <= cn;ii ++) {
			int t = tre[1].id;
			if(!t) break;
			dp[t] = tre[1].nm;
			ans += dp[t];
			me = Min(me,dp[t] + a[b[t]]);
			addtree(t,it());
			int nmn = min((LL)0x3f3f3f3f,0ll+dp[t]+a[b[t]]+mn);
			MN(tre[1],nmn);
			lz[1] = Min(lz[1],nmn);
			int pr = 0;
			for(int j = 0;j < (int)g[t].size();j ++) {
				int y = rk[g[t][j].FI],w = g[t][j].SE;
				addsg(pr+1,y-1,dp[t] + a[b[t]]);
				pr = y; addsg(y,y,dp[t]+w);
			}
			addsg(pr+1,cn,dp[t] + a[b[t]]);
		}
		ans += me*1ll*(n-cn);
	}
	AIput(ans,'\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值