[BZOJ 2801]POI2012 Minimalist Security

25 篇文章 0 订阅

首先不要被题目吓倒,这题你就做出大半了。

只有联通块内才会互相约束(废话)

扫描每一个联通块,找到一个起点,假设他的权值为x。

那么每个连出去的边的终点权值为  w-x 。也就是说每个点上一定为kx+b(其中k=1或-1)

也许每个点会被扫描到多次,分以下几种情况

1、k1==k2,若b1!=b2无解,若b1==b2.......没有影响,继续running

2、k1!=k2,那么我们可以算出x的值,把这个值带入算出连通块里的点的值,判断有无解即可。

如果扫描途中没有遇到什么意外(就是1、2的情况)那么利用么个点i的不等式连立不等式组0<=ki x+bi<=pi解出x的范围

注意解集是否为空集,这里也可能出现无解。

最后我们可以看到代价是x的一次函数,也就是说最值在边界出取得,判断一下就好了。

复杂度O(n+m)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int Maxn=6000005, Max=500005;
int node[Maxn],next[Maxn],a[Max],q[Max],x,y,z,n,m,l,r,tot,i;
LL len[Maxn],dig[Max],w[Max],total,maxx,minx,cnt1,cnt2,dw,up,data,t;
bool v[Max],flag;
struct arr { int w,s; } d[Max],tmp;

int read(){
  char ch=getchar();
  int ret=0;
  while (ch<'0' || ch>'9') ch=getchar();
  while (ch>='0' && ch<='9')
    {ret=ret*10+ch-'0'; ch=getchar();}
  return ret;
}

void add(int x,int y,int z){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot; len[tot]=z;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot; len[tot]=z;
}

bool Judge(int x){
  for (q[l=r=1]=x,total=0;l<=r;l++){
  	v[q[l]]=1;
  	if (dig[q[l]]<0 || dig[q[l]]>w[q[l]]) return 0;
  	total+=(LL)w[q[l]]-dig[q[l]];
  	for (int i=a[q[l]];i;i=next[i]){
  	  if (dig[node[i]]==-1)
	    dig[ q[++r]=node[i] ]=len[i]-dig[ q[l] ];
	  else if (dig[q[l]]+dig[node[i]]!=len[i])
	    return 0;
    }
  }
  return 1;
}

void bfs(int x){
  d[x].w=0; d[x].s=1; v[x]=1;
  for (q[l=r=1]=x,data=-1;l<=r;l++){
  	
  	for (int i=a[q[l]];i;i=next[i]){
  	  if (v[node[i]]){
  	  	
  	  	tmp.w=len[i]-d[node[i]].w;
  	  	tmp.s=(d[node[i]].s^1);
  	  	if (tmp.s==d[q[l]].s){
  	  	  if (tmp.w!=d[q[l]].w)
  	  	    {flag=0; break;}
  	  	  else continue;
  	  	} else
  	  	{
  	  	  if (tmp.s==0) t=-1; else t=1;
  	  	  if ( (d[q[l]].w-tmp.w)/t<0 ) {flag=0; break;}
  	  	  if ( ( (d[q[l]].w-tmp.w)/t )%2!=0 ) {flag=0; break;}
  	  	    else {data = (d[q[l]].w-tmp.w)/t/2; break;}
  	  	}
  	  	
  	  } else
  	  {
  	  	d[node[i]].w=len[i]-d[q[l]].w;
  	  	d[node[i]].s=(d[q[l]].s^1);
  	  	v[q[++r]=node[i]]=1;
  	  }
  	  
    }
    
    if (flag==0 || data>=0) break;
  }
  
  if (data>=0){
  	dig[x]=data;
  	if (Judge(x)){
  	  minx+=total;
  	  maxx+=total;
  	} else {flag=0;return;}
  	return;
  }

  if (flag==0) return;

  dw=0; up=1000000000;
  for (int i=1;i<=r;i++){
  	tmp=d[q[i]];
  	if (tmp.s==1){
	  up=min(up,(LL)w[q[i]]-tmp.w);
	  dw=max(dw,(LL)-tmp.w);
    } else
	{
	  up=min(up,(LL)tmp.w);
	  dw=max(dw,(LL)tmp.w-w[q[i]]);
    }
  }
  if (up<dw){ flag=0; return; }
  cnt1=cnt2=0;
  for (int i=1;i<=r;i++){
  	if (d[q[i]].s==1){
  	  cnt1+=(LL)w[q[i]]-(d[ q[i] ].w+dw);
  	  cnt2+=(LL)w[q[i]]-(d[ q[i] ].w+up);
    } else
    {
      cnt1+=(LL)w[q[i]]-(d[ q[i] ].w-dw);
  	  cnt2+=(LL)w[q[i]]-(d[ q[i] ].w-up);
    }
  }
  minx+=min(cnt1,cnt2);
  maxx+=max(cnt1,cnt2);
}

int main(){
  freopen("bez.in","r",stdin);
  freopen("bez.out","w",stdout);
  //scanf("%d%d",&n,&m);
  n=read(); m=read();
  for (i=1;i<=n;i++) w[i]=read(); //scanf("%d",&w[i]);
  for (i=1;i<=m;i++){
  	//scanf("%d%d%d",&x,&y,&z);
  	x=read(); y=read(); z=read();
  	add(x,y,z);
  }
  memset(dig,-1,sizeof(dig));
  for (i=1,flag=1;i<=n;i++)
  if (!v[i]){
    bfs(i);
    if (flag==0) break;
  }
  if (flag==0) puts("NIE");
   else printf("%I64d %I64d\n",minx,maxx);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值