BZOJ2801: [Poi2012]Minimalist Security

题目大意:一张n个点m条边的无向图,有点权有边权都是非负,且每条边的权值小于等于两个顶点的权值和,现在要将每个点减一个非负整数使得每条边权等于两个顶点的点权和,问最大修改代价和最小修改代价

首先对于每一个连通块假如确定了一个点的值,那么所有其他点的权值就都确定了

所以部门可以随便找到一个点设他的权值是x,然后把其他点全部用x表示出来,这样同时根据这个点的修改上限为x圈定一个取值范围,这样就可以判断合不合法了

然后在合法范围内求最大值和最小值就可以了


这题强制用读入优化,直接用scanf会RE


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define N 500010
#define M 6000010
using namespace std;
char BB[1<<15],*K=BB,*T=BB;
#define getc() (K==T&&(T=(K=BB)+fread(BB,1,1<<15,stdin),K==T)?0:*K++)
inline long long read()
{
    long long x=0;char ch=getc();
    while(ch<'0'||ch>'9')ch=getc();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getc();
    return x;
}
long long a[N];
bool used[N];
long long to[M],nxt[M],w[M],pre[N],cnt;
void ae(long long ff,long long tt,long long ww)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
	w[cnt]=ww;
}
long long o[N],v[N];
long long q[N],h,t;
long long MIN,MAX;
void solve(long long S,long long &tmp1,long long &tmp2)
{
	h=t=1;q[1]=S;o[S]=1;v[S]=0;
	long long O=0,V=0;
	used[S]=true;
	MIN=0;MAX=a[S];
	long long i,j,x,y;
	while(h<=t)
	{
		x=q[h];h++;
		O+=-o[x];V+=a[x]-v[x];
		for(i=pre[x];i;i=nxt[i])
		{
			j=to[i];
			if(!used[j])
			{
				used[j]=true;
				t++;q[t]=j;
				o[j]=-o[x];
				v[j]=w[i]-v[x];
				if(o[j]==1)
				{
					MAX=min(MAX,a[j]-v[j]);
					MIN=max(MIN,-v[j]);
				}
				else
				{
					MAX=min(MAX,v[j]);
					MIN=max(MIN,v[j]-a[j]);
				}
				if(MIN>MAX)
				{
					puts("NIE");
					exit(0);
				}
			}
			else
			{
				if(o[j]==o[x])
				{
					if((v[j]+v[x]-w[i])%2!=0)
					{
						puts("NIE");
						exit(0);
					}
					else
					{
						MAX=min(MAX,(w[i]-v[x]-v[j])/2/o[x]);
						MIN=max(MIN,(w[i]-v[x]-v[j])/2/o[x]);
						if(MIN>MAX)
						{
							puts("NIE");
							exit(0);
						}
					}
				}
				else
				{
					if(w[i]!=v[x]+v[j])
					{
						puts("NIE");
						exit(0);
					}
				}
			}
		}
	}
	if(O>0) tmp1+=MIN*O+V,tmp2+=MAX*O+V;
	else tmp1+=MAX*O+V,tmp2+=MIN*O+V;
}
int main()
{
	long long n,m;
	n=read();m=read();
	long long i,j,x,y,z;
	for(i=1;i<=n;i++)
	a[i]=read();
	for(i=1;i<=m;i++)
	{
		x=read();y=read();z=read();
		ae(x,y,z);ae(y,x,z);
	}
	long long minn=0,maxn=0;
	for(i=1;i<=n;i++)
	if(!used[i])
	solve(i,minn,maxn);
	printf("%lld %lld",minn,maxn);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值