BZOJ.4500.矩陣(差分約束 SPFA判負環 / 帶權並查集)

BZOJphp


差分約束:html

我是誰,差分約束是啥,這是哪git

太真實了= =  插個廣告:這裏有差分約束詳(並不)解spa

\(r_i\)爲第\(i\)行總體加了多少的權值,\(c_i\)爲第\(i\)列總體加了多少權值,那麼限制\((i,j),k\)就是\(r_i+c_j=k\)
這就是差分約束裸題了。\(r_i+c_j=k\Rightarrow r_i-(-c_j)\leq k\ \&\&\ -c_j-r_i\leq -k\)
注意形式是\(x_j-x_i\leq w\)=v=
建邊跑最短路判負環便可。.net

去洛谷複習之前的板子(忘了怎麼寫了=-=),發現DFS判負環被卡掉了?太棒啦不用背DFS的代碼惹。
乖乖寫BFS好了。pwa


帶權並查集:
發現這題和BZOJ1202是如出一轍的= =。由於全是相等關係,實際上是十分特殊的差分約束,能夠用帶權並查集作。
\(fa[x]\)表示\(x\)所在集合的根節點,\(dis[x]\)表示\(x\)\(fa[x]\)的實際距離。
所謂距離是指:對於\(r+c=k\),變成\(r-(-c)=k\),即\(r\)\(-c\)\(k\),就在\(r\to -c\)之間連距離爲\(k\)的邊,同時令\(fa[r]=-c\)
這樣對於一個限制\(r,c,k\),若是\(r,c\)不在同一集合就合併(令較大的數的祖先是較小的數)。在並查集\(Find\)過程當中順便維護一下\(dis\)(具體見代碼好惹,注意變量賦值順序)。
若是\(r,c\)在同一集合,就根據\(dis\)差判一下它倆的距離是否等於\(k\)code

就算\(k\)多是負的這麼作也沒什麼問題。(廢話=v=)htm


差分約束:blog

//904kb 48ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=2005;

int Enum,H[N],nxt[N],to[N],len[N],dis[N],dgr[N];
bool vis[N],inq[N];

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now*f;
}
inline void AE(int u,int v,int w)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
}
bool SPFA(int s,int n)//怎麼都直接拿n作總點數的=-= 強迫症表示不行 
{
    std::queue<int> q;
    q.push(s), dis[s]=0;
    while(!q.empty())
    {
        int x=q.front(); q.pop();
        inq[x]=0;
        for(int i=H[x],v; i; i=nxt[i])
            if(dis[v=to[i]]>dis[x]+len[i])
            {
                if(++dgr[v]>n) return 1;
                dis[v]=dis[x]+len[i], !inq[v]&&(q.push(v),inq[v]=1);
            }
    }
    return 0;
}

int main()
{
    for(int Ts=read(); Ts--; )
    {
        int n=read(),m=read(),tot=n+m,cnt=0;
        Enum=0, memset(H,0,tot+1<<2), memset(vis,0,tot+1);
        for(int u,v,w,K=read(); K--; )
        {
            u=read(),v=read()+n,w=read();
            AE(v,u,w), AE(u,v,-w);
            if(!vis[u]) vis[u]=1, ++cnt;
            if(!vis[v]) vis[v]=1, ++cnt;
        }
        memset(dis,0x7f,tot+1<<2), memset(dgr,0,tot+1<<2), memset(inq,0,tot+1);
        bool fg=1;
        for(int i=1; i<=tot; ++i)
            if(vis[i]&&dis[i]==dis[0]&&SPFA(i,cnt)) {fg=0; break;}
        puts(fg?"Yes":"No");
    }

    return 0;
}

帶權並查集:get

//836kb 20ms
#include <cstdio>
#include <cctype>
#include <assert.h>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=2005;

int fa[N],dis[N];

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now*f;
}
int Find(int x)
{
    if(x==fa[x]) return x;
    int t=fa[x];
    fa[x]=Find(t), dis[x]+=dis[t];//!!!
    return fa[x];
}

int main()
{
    for(int Ts=read(); Ts--; )
    {
        const int n=read(),m=read(),tot=n+m;
        for(int i=1; i<=tot; ++i) fa[i]=i, dis[i]=0;
        bool fg=1;
        for(int u,v,w,K=read(); K--; )
        {
            u=read(),v=read()+n,w=read();
            if(!fg) continue;
            int r1=Find(u),r2=Find(v);
            if(r1!=r2) fa[r1]=r2, dis[r1]=dis[v]+w-dis[u];
            else if(dis[u]-dis[v]!=w) fg=0;
        }
        puts(fg?"Yes":"No");
    }

    return 0;
}
相關文章
相關標籤/搜索