正题
题目链接:http://noip.ybtoj.com.cn/contest/105/problem/3
题目大意
nnn个点mmm条边的图,保证每条边两边的点权和大于等于边权。现在要去减去最少/多的点权使得每条边的边权等于两边的点权。
解题思路
对与一个连通块,显然确定一个就可以确定别的,但是我们无法枚举这个。所以我们设第一个为xxx,然后后面的都可以表示成kx+bkx+bkx+b的形式,之后因为每个点的点权有限制,这样我们就可以确定xxx的范围。
对与环的情况判断一下k1x+b1=k2x+b2k_1x+b_1=k_2x+b_2k1x+b1=k2x+b2的方程解的情况即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define ll long long
using namespace std;
const ll N=5e5+10;
struct node{ll to,next,w;
}a[N*12];
ll n,m,p[N],k[N],b[N],sumk,sumb;
ll l,r,maxs,mins,tot,ls[N];
bool v[N],vis[N];
ll read() {ll x=0,f=1; char c=getchar();while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();return x*f;
}
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
ll check(ll K,ll B,ll x){if((K-k[x])==0){if(b[x]-B)return 0;return 1;}if((b[x]-B)%(K-k[x])!=0)return 0;ll w=(b[x]-B)/(K-k[x]);if(l>w||w>r)return 0;l=w;r=w;return 1;
}
bool dfs(ll x){v[x]=1;sumk+=k[x];sumb+=b[x];if(k[x]==1){l=max(l,-b[x]);r=min(r,p[x]-b[x]);if(l>r)return 0;}else{l=max(l,b[x]-p[x]);r=min(r,b[x]);if(l>r)return 0;}for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(v[y]){if(!check(-k[x],a[i].w-b[x],y))return 0;}else{k[y]=-k[x];b[y]=a[i].w-b[x];if(!dfs(y))return 0;}}return 1;
}
int main()
{
// freopen("diamond.in","r",stdin);
// freopen("diamond.out","w",stdout);n=read();m=read();ll sum=0;for(ll i=1;i<=n;i++)sum+=(p[i]=read());for(ll i=1;i<=m;i++){ll x=read(),y=read(),w=read();addl(x,y,w);addl(y,x,w);}for(ll i=1;i<=n;i++){if(v[i])continue;l=sumk=sumb=0;r=p[i];k[i]=1; if(!dfs(i))return printf("NIE\n")&0;else{if(sumk<0)maxs+=l*sumk+sumb,mins+=r*sumk+sumb;else maxs+=r*sumk+sumb,mins+=l*sumk+sumb;}}printf("%lld %lld\n",sum-maxs,sum-mins);
}