BZOJ2801: [Poi2012]Minimalist Security

題目大意:
一張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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章