ABC404G 题解
G. Specified Range Sums
题意
有三个长度为 M M M 的序列 L , R , S L,R,S L,R,S,你要判断是否存在一个长度为 N N N 的 正整数 序列 A A A,满足以 ∑ j = L i R i A j = S i \sum_{j=L_i}^{R_i} A_j=S_i ∑j=LiRiAj=Si。
若存在,找到最小的 ∑ j = 1 N A j \sum_{j=1}^N A_j ∑j=1NAj;否则,输出 -1
。
思路
首先,我们考虑将求和转换为前缀和,即定义 C i = ∑ j = 1 i A j C_i=\sum_{j=1}^iA_j Ci=∑j=1iAj,则 C R i − C L i − 1 = S i C_{R_i}-C_{L_i-1}=S_i CRi−CLi−1=Si。
建立有向图,顶点编号为 0 ∼ n 0 \sim n 0∼n,这样连边: ( L i − 1 , R i ) = S i (L_i-1,R_i)=S_i (Li−1,Ri)=Si, ( R i , L i − 1 ) = − S i (R_i,L_i-1)=-S_i (Ri,Li−1)=−Si。另外,由于是正整数序列,所以 ( i + 1 , i ) = − 1 (i+1,i)=-1 (i+1,i)=−1。
我们需要计算 n → 0 n \rightarrow 0 n→0 的最短路,答案即为这个值的相反数。
注意:无解时图中有负环,所以 Dijkstra \text{Dijkstra} Dijkstra 不可以。考虑可以处理负环的 Bellman-Ford \text{Bellman-Ford} Bellman-Ford 算法(不会没关系,下面讲):
与图上动态规划相似,定义 d i s i dis_i disi 表示 从 n n n 到 i i i 的最短路, d i s n = 0 dis_n=0 disn=0,其余为 ∞ \infty ∞。
共进行 N N N 次操作,每次操作如下:
- 对于每一条有向边 ( u , v ) = w (u,v)=w (u,v)=w, d i s v = min ( d i s v , d i s u + w ) dis_v=\min(dis_v,dis_u+w) disv=min(disv,disu+w),共 M M M 条边。
复杂度为 O ( N M ) O(NM) O(NM),通常把上述操作称作 松弛(relax)。
在这 N N N 次松弛之后,再执行第 N + 1 N+1 N+1 次操作,若还可以继续执行松弛操作,就说明图中存在负环,无解,输出 − 1 -1 −1。
最终答案即为 − d i s 0 -dis_0 −dis0。
C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=3e18;
const int maxn=4005;
int n,m;
struct Node{int u,v,w;
};
vector<Node> v;
int dis[maxn];
signed main(){cin>>n>>m;//建图 连边for(int i=1;i<=m;i++){int l,r,s;cin>>l>>r>>s;v.push_back({l-1,r,s});v.push_back({r,l-1,-s});}for(int i=0;i<n;i++) v.push_back({i+1,i,-1});//初始化for(int i=1;i<=n;i++) dis[i]=inf;dis[n]=0;//Bellman-Ford计算最短路 直接将第N+1次操作放入循环中for(int i=1;i<=n+1;i++){for(Node e:v){if(dis[e.v]>dis[e.u]+e.w){if(i==n+1){//若已经执行完n+1次松弛还可以继续执行,则无解cout<<-1<<endl;return 0;}dis[e.v]=dis[e.u]+e.w;}}}cout<<-dis[0]<<endl; return 0;
}