競賽圖詳解


競賽圖是有向完全圖,我見到的題包括給定一個競賽圖或者是競賽圖的計數問題。

首先給出兩個結論:

1>:任意競賽圖都有哈密頓路徑(經過每個點一次的路徑,不要求回到出發點)。

2>:競賽圖存在哈密頓回路的充要條件是強聯通。

 

 

顯然如果我們可以證明出結論2的話,對於一般競賽圖的哈密頓路徑我們只需tarjan按照拓撲順序得到。

結論2證明:

用歸納法,顯然對於n=1,2的情況是滿足結論的,那么我們證明n>=3時只需要由1-n-1都滿足這個性質來推即可。

1>:

若在原n-1個點構成的強聯通的競賽圖中新添加的一個點。

原圖一定存在一個哈密頓回路,並且第n個點與前n-1個點之間的邊一定不是同向的(滿足一個強聯通分量)。

所以將環上的點依次排列,一定存在一個點p,使得存在邊p->n&&n->nxt[p]或n->p&&nxt[p]->n。

這樣就可以直接把n加進去了。

2>:

若原n-1個點不是一個強聯通分量。

按照拓撲序,找到head,tail。n一定存在tail中一個點p存在邊p->n,同理head中一定有一個點p存在邊n->p。

這樣就直接將哈密頓鏈首位連接了。

 

 

哈密頓路徑構造:

首先記錄當前鏈的head,tail。

依次枚舉剩下的點加入。

1>:x->head,直接把head改成x。

2>:tail->x,直接把tail改成x。

3>:否則,一定存在head->x,x->tail。即一定存在一個點p使得p->x,x->nxt[p]。

 

 

通過哈密頓路徑構造哈密頓回路:

首先找到一個最靠右的點p,滿足p->head。

然后就從p之后點的開始加入環內。

我們考慮會存在一種情況是現在環上所有點都指向p,那么p肯定是通過之后的一個點連接到環內。

所以我們記錄一個pre代表插入的鏈的左端點,當p滿足到環上某個點時,就把[pre,p]這個鏈插入到環中。

因為[pre,p)與環上的點之間的邊都是指向自己的,所以一定存在環上一個點x滿足x->pre&&p->nxt[x],然后插入即可。

 

 

計數題:

bzoj 5219

codeforces 913 F Strongly Connected Tournament

codeforces 804 F Fake bullions (神仙題)

 

 

hdu 5503 EarthCup:

題目描述:給定每個點的獲勝次數,問是否存在這樣的競賽圖。

顯然會想到一個網絡流做法,左邊n*(n-1)/2個點,右邊n個點跑最大流即可。

繼續推,我們可以把右邊第i個點展開成a[i]個流量為一的點。

二分圖完美匹配。Hall定理,一個二分圖存在完美匹配條件是任意一個集合匹配點的個數>=size。

因為右邊的a[i]個點的集合都一樣,所以邊界肯定是都選上這a[i]個點。

所以滿足條件:

$\sum_{i\epsilon S}a[i]\leq |S|*(n-1)-|S|*(|S|-1)/2$

所以直接排序之后從大到小前綴和即可。

wiki上給的是從小到大排序滿足

$\sum_{i\epsilon S}a[i]\geq |S|*(|S|-1)/2$

兩種是一樣的。

 

 

hdu 5961 傳遞:

顯然是出環就不對了。

然后一個圖中若a->b,b->c結果ac之間的邊在另一個圖里也不行。

其實就是PUQ和PUQ'(Q的邊都反向)沒有環即可。不用對Q做,因為邊都反向結果相同。

 

 

bzoj 4727:

就是一個裸的應用了。

能經過的點的個數就是拓撲序上后面的所有點的個數。

然后一次輸出同一個強聯通分量上的環即可。

#include <bits/stdc++.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define FOR2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
char xch,xB[1<<15],*xS=xB,*xTT=xB;
#define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++)
inline int read() {
    int x=0,f=1;char ch=getc();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
 
inline void write(int x) {
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
 
#define M 2005
int n;
bool a[M][M],re[M][M];
int low[M],dfn[M],sta[M],clock_;
int dep[M],be[M],nxt[M],dui[M],size[M],pos[M];
vector <int> buc[M];
 
inline void tarjan(int x) {
    low[x]=dfn[x]=++clock_;
    sta[++sta[0]]=x;
    for1(1,n,i) {
        if(!a[x][i]) continue;
        if(!low[i]) {
            tarjan(i);
            low[x]=min(low[x],low[i]);
        }
        else if(!be[i]) low[x]=min(low[x],dfn[i]);
    }
    if(low[x]==dfn[x]) {
        ++be[0];
        dep[be[0]]=1;
        while (1) {
            int tmp=sta[sta[0]--];
            be[tmp]=be[0];
            buc[be[0]].push_back(tmp);
            if(tmp==x) break;
        }
    }
}
 
inline void print(int h) {
    write(h);
    putchar(' ');
    for(int i=nxt[h];i!=h;i=nxt[i]) {
        write(i);
        putchar(' ');
    }
}
 
int main () {
    //freopen("a.in","r",stdin);
    n=read();
    for1(1,n-1,i) for1(1,i,j) {
        bool t=read();
        a[j][i+1]=t;
        a[i+1][j]=t^1;
    }
    for1(1,n,i) a[i][i]=1;
    for1(1,n,i) if(!low[i]) tarjan(i);
    for1(1,n,i) for1(1,i-1,j) if(be[i]!=be[j]) {
        int x=i,y=j;
        if(!a[x][y]) swap(x,y);
        if(!re[be[x]][be[y]]) re[be[x]][be[y]]=1,++dep[be[y]];
    }
     
    for1(1,be[0],i) pos[dep[i]]=i;
    FOR2(be[0],1,i) size[pos[i]]=size[pos[i+1]]+buc[pos[i]].size();
     
    for1(1,be[0],i) {
        int h,t;
        h=t=buc[i][0];
        int size=buc[i].size();
        for1(1,size-1,j) {
            int x=buc[i][j];
            //找哈密頓鏈
            if(a[x][h]) nxt[x]=h,h=x;
            else if(a[t][x]) nxt[t]=x,t=x;
            else {
                for(int k=h;k!=t;k=nxt[k]) {
                    if(a[k][x]&&a[x][nxt[k]]) {
                        nxt[x]=nxt[k];
                        nxt[k]=x;
                        break;
                    }
                }
            }
        }
        size=0;
        for(int j=h,num;j;j=num) {
            dui[++size]=j;
            num=nxt[j],nxt[j]=0;
        }
        FOR2(size,1,j) if(a[dui[j]][h]) {
            for1(1,j-1,k) nxt[dui[k]]=dui[k+1];
            nxt[dui[j]]=h,t=j;
            break;
        }
        //由鏈變成環
        int pre=0;
        for1(t+1,size,j) {
            if(!pre) pre=j;
            bool J=0;
            for1(1,pre-1,k) if(a[dui[j]][dui[k]]) {J=1;break;}
            if(!J) continue;
            for1(pre,j-1,k) nxt[dui[k]]=dui[k+1];
            for1(1,pre-1,k) if(a[dui[k]][dui[pre]]&&a[dui[j]][nxt[dui[k]]]) {
                nxt[dui[j]]=nxt[dui[k]];
                nxt[dui[k]]=dui[pre];
                pre=0;
                break;
            }
        }
    }
     
    for1(1,n,i) {
        write(size[be[i]]);
        putchar(' ');
        print(i);
        for1(dep[be[i]]+1,be[0],j) print(buc[pos[j]][0]);
        puts("");
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM