BZOJ2801/POI2012 Minimalist Security

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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章