BZOJ2801/POI2012 Minimalist Security

来源:互联网 发布:戴尔公司待遇 知乎 编辑:程序博客网 时间:2024/05/17 01:55

Task
给出一个N个顶点、M条边的无向图,边(u,v)有权值w(u,v),顶点i也有权值p(i),
并且对于每条边(u,v)都满足p(u)+p(v)>=w(u,v)。
现在要将顶点i的权值减去z(i),其中0<=z(i)<=p(i)。
修改后设顶点i的权值p’(i)=p(i)-z(i),对于每条边(u,v)都满足p’(u)+p’(v)=w(u,v)。
求sum{z(i)}的最小值和最大值。
n<=500,000, m<=3,000,000, 0<=p(i)<=10^6, 0<=w<=10^6.

Solution
对于每一条边(u,v):z[u]+z[v]是确定的,也就是确定了端点中的一个,另一个也可以确定了.那么假如确定联通块内任意一点的值,整个联通块都可以确定下来了.
假设设联通块任意一点x的值为a,那么联通块剩下的点k都可以用来y*a+z表示.通过0≤y*a+z≤p[k]求出a的范围.对于这个联通块,求出∑z[k]关于a的表达式,利用a的范围求出最值.
只要遍历一遍图即可,复杂度O(m+n).
当然还有一种做法,通过并查集,维护每个点到它所在树根(假设树根的值为a)的表达式:ya+z.
每次对一条边(u,v):
假如u,v已经联通,可以直接求出a的值.
否则合并两棵树.

//对于并查集的做法,需要计算的地方有点多不易调试,比赛时最好选取第一种做法.

#include<cstdio>#include<cstring>#include<algorithm>#define ll long long#include<iostream>using namespace std;const int M=5e5+5;int head[M],rt,ec=0,f[M],n,p[M],flag=1,tot,A=0,m;ll val[M];ll mx=0,mi=0,L,R,B=0;struct  node{    int to,v,nex;}e[M*12];inline void rd(int &res){    res=0;char c;    while(c=getchar(),c<48);    do res=(res<<1)+(res<<3)+(c^48);    while(c=getchar(),c>=48);}void ins(int a,int b,int c){    e[ec]=(node){b,c,head[a]};    head[a]=ec++;    e[ec]=(node){a,c,head[b]};    head[b]=ec++;   }bool dfs(int x,int par){    ll a=(p[x]-val[x])/f[x],b=-val[x]/f[x];    //0<=f[x]*a+val[x]<=p[x]    //a<=p[x]-val[x]/f[x]        //a>=-val[x]/f[x]    A+=f[x];    B+=val[x];    if(f[x]>0)L=max(L,b),R=min(R,a);    else L=max(L,a),R=min(R,b);    if(L>R)return false;    for(int i=head[x];~i;i=e[i].nex){        int to=e[i].to,v=e[i].v;//        if(to==par)continue;        ll t=v-val[x];        if(f[to]!=0){            if(f[to]==-f[x]&&val[to]!=t)return false;            else if(f[to]!=-f[x]){                if((val[to]-t)%(-f[x]-f[to])!=0)return false;                ll res=(val[to]-t)/(-f[x]-f[to]);                L=max(L,res);//f[to]+val[to]=f[x]+val[x]                R=min(R,res);                if(L>R)return false;            }        }        else{            f[to]=-f[x];            val[to]=t;            if(!dfs(to,x)){return false;}        }    }    return true;}bool solve(){    for(int i=1;i<=n;i++){        if(f[i]==0){            L=-1e18,R=1e18;            A=B=0;            f[i]=1;            val[i]=0;            if(!dfs(i,i))return false;            ll a=L*A+B,b=R*A+B;            mi+=min(a,b);            mx+=max(a,b);        }    }    return true;}int main(){    memset(head,-1,sizeof(head));    int i,j,a,b,c;    rd(n);rd(m);    for(i=1;i<=n;i++)rd(p[i]);    for(i=1;i<=m;i++){        rd(a);rd(b);rd(c);        ins(a,b,p[a]+p[b]-c);    }    if(!solve())puts("NIE");    else cout<<mi<<" "<<mx<<endl;    return 0;}

并查集:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<ctime>#include<cstdlib>#include<string>#include<cmath>#include<map>#include<set>#include<queue>#include<bitset>#define ll long long#define lsy puts("lay")#define rep(i,a,b) for((i)=(a);(i)<=(b);(i)++)#define per(i,a,b) for((i)=(b);(i)>=(a);(i)--)using namespace std;inline void rd(int &res){    res=0;char c;    while(c=getchar(),c<48);    do res=(res<<1)+(res<<3)+(c^48);    while(c=getchar(),c>=48);}inline void print(int k){    if(!k)return;    print(k/10);    putchar(k%10^48);}inline void sc(int k){    if(k<0){k=-k;putchar('-');}    print(k);    if(k==0)putchar('0');//    putchar('\n');}inline void Max(int &x,int y){if(x<y)x=y;}inline void Min(int &x,int y){if(x>y)x=y;}const int maxn=500005;const int maxm=3000005;int u[maxm],v[maxm],e[maxm],fa[maxn],f[maxn],n,m,h[maxn],p[maxn];ll mi=0,mx=0,sum[maxn],rt[maxn],val[maxn],l[maxn],r[maxn];int get(int x){    if(fa[x]!=x){        int a=f[x],b=fa[x];        fa[x]=get(fa[x]);        val[x]=val[b]*a+val[x];        f[x]=f[b]*a;    }    return fa[x];}bool solve(){    int i,j,k,a,b,c;    ll d,t;    for(i=1;i<=m;i++){        a=get(u[i]),b=get(v[i]);        if(a!=b){            val[b]=f[v[i]]*(-val[u[i]]+e[i]-val[v[i]]);            f[b]=-f[u[i]]*f[v[i]];            fa[b]=a;            if(~rt[b]){//rt[b]=rt[a]*f[b]+val[b]                c=(rt[b]-val[b])*f[b];                if(~rt[a]&&rt[a]!=c) return false;                rt[a]=c;            }            continue;        }        c=f[u[i]]+f[v[i]];        d=e[i]-(val[u[i]]+val[v[i]]);//c*rt=d        if(c!=0&&d%c!=0){return false;}        if(c){            t=d/c;            if(rt[a]==-1)rt[a]=t;            else if(rt[a]!=t){                return false;            }        }    }    for(i=1;i<=n;i++)l[i]=0,r[i]=p[i];    for(i=1;i<=n;i++){        a=get(i);//0<=f[i]*rt+val[i]<=p[i]'        if(f[i]>0){//rt<=p[i]-val[i]            r[a]=min(r[a],p[i]-val[i]);            l[a]=max(l[a],-val[i]);        }        else {//0<=-rt+val[i]<=p[i]            r[a]=min(r[a],val[i]);            l[a]=max(l[a],val[i]-p[i]);        }        h[a]+=f[i];        sum[a]+=val[i];    }    for(i=1;i<=n;i++){        if(fa[i]==i){            a=i;            if(l[a]>r[a]){                return false;            }if(~rt[a]){                if(rt[a]>r[a]||rt[a]<l[a]){                    return false;}                mi+=rt[a]*h[a]+sum[a],mx+=rt[a]*h[a]+sum[a];            }            else {                ll b=h[a]*l[a]+sum[a],c=h[a]*r[a]+sum[a];                mi+=min(b,c);mx+=max(b,c);            }        }    }    return true;}int main(){    int i,j,k;    memset(rt,-1,sizeof(rt));    memset(val,0,sizeof(val));    rd(n);rd(m);    for(i=1;i<=n;i++){        fa[i]=i,rd(p[i]);        f[i]=1;    }    for(i=1;i<=m;i++){        rd(u[i]);rd(v[i]);rd(e[i]);        e[i]=p[u[i]]+p[v[i]]-e[i];    }    if(!solve())puts("NIE");    else cout<<mi<<" "<<mx<<endl;    return 0;}
0 0