题目大意:
一张n个点m条边的无向图,有点权有边权都是非负,且每条边的权值小于等于两个顶点的权值和,现在要将每个点减一个非负整数使得每条边权等于两个顶点的点权和,问最大修改代价和最小修改代价
题解:
每一个连通块如果确定了一个点的值,那么所有其他点的权值就都确定了。
那么我们dfs每个联通块,随便设一个点为x,那联通块内每个点的值都可以用x表示出来,顺便x有个范围,若范围不存在就NIE不然就是个一次函数范围内求最值,初中数学即可。
#include<bits/stdc++.h>
using namespace std;
const int N=500005,M=3000005;
typedef long long ll;
ll read(){
ll x=0,f=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
struct Edg{
ll nxt,poi,cost;
}e[M<<1];
ll n,m,first[N],l=0;
ll a[N],b[N],k[N],ans1=0,ans2=0;
bool vis[N];
void addedge(ll u,ll v,ll k){
l++;
e[l].nxt=first[u];
e[l].poi=v;
e[l].cost=k;
first[u]=l;
}
void solve(ll u){
b[u]=0; k[u]=1;
ll K=0,B=0,mx=a[u],mn=0;
queue<int>q;
q.push(u); vis[u]=1;
while (!q.empty()){
ll x=q.front(); q.pop();
K+=-k[x]; B+=a[x]-b[x];
for (ll p=first[x];p;p=e[p].nxt){
ll v=e[p].poi;
if (!vis[v]){
k[v]=-k[x]; b[v]=e[p].cost-b[x]; vis[v]=1;
q.push(v);
if (k[v]==1){
mx=min(mx,a[v]-b[v]);
mn=max(mn,-b[v]);
} else{
mx=min(mx,b[v]);
mn=max(mn,b[v]-a[v]);
}
if (mx<mn){
printf("NIE\n"); exit(0);
}
}else{
if (k[x]==k[v]){
if ((b[v]+b[x]-e[p].cost)%2!=0){
printf("NIE\n"); exit(0);
} else{
mx=min(mx,(e[p].cost-b[v]-b[x])/2/k[x]);
mn=max(mn,(e[p].cost-b[v]-b[x])/2/k[x]);
if (mx<mn){
printf("NIE\n"); exit(0);
}
}
}else{
if (e[p].cost-b[x]!=b[v]){
printf("NIE\n"); exit(0);
}
}
}
}
}
if (K>0) ans1+=mn*K+B,ans2+=mx*K+B;
else ans1+=mx*K+B,ans2+=mn*K+B;
}
int main(){
n=read(),m=read();
for (ll i=1;i<=n;i++) a[i]=read();
for (ll i=1;i<=m;i++){
ll u=read(),v=read(),w=read();
addedge(u,v,w); addedge(v,u,w);
}
for (ll i=1;i<=n;i++){
if (!vis[i]) solve(i);
}
printf("%lld %lld\n",ans1,ans2);
return 0;
}