題解-AtCoder ARC-083F Collecting Balls

Problem

ARC083Fgit

題意概要:給定 \(2n\) 個二維平面上的球,座標分別爲 \((x_i,y_i)\),並給出 \(n\)\(A\)類 機器人 和 \(n\)\(B\)類 機器人,其中:學習

  • \(A\)加5分 機器人分佈在橫座標上,座標依次爲 \((1,0),(2,0),\cdots ,(n,0)\),觸發第 \(i\) 個機器人,它會將位處第 \(i\) 列的最下頭的球拿走(即橫座標爲 \(i\) 且縱座標最小的球)
  • \(B\)不加分 機器人分佈在橫座標上,座標依次爲 \((0,1),(0,2),\cdots ,(0,n)\),觸發第 \(i\) 個機器人,它會將位處第 \(i\) 行的最左側的球拿走(即縱座標爲 \(i\) 且橫座標最小的球)

如今須要依次觸發這 \(2n\) 個機器人,(每一個機器人只能被觸發一次),問有多少種觸發機器人的方式能將全部球拿完(共 \((2n)!\) 種可能狀況)spa

\(2\le n\le 10^5,\ 1\le x_i,y_i\le n\)code

Solution

作完這道毒瘤題,17年的做業終於完成了! (。・∀・)ノ゙get

往上一翻發現本身是從一半開始作的 இ皿இit

乍一看沒有啥子想法io

考慮到一個球 \((x,y)\),它有兩種被清除的方式:被第 \(x\)\(A\) 給除掉;被第 \(y\)\(B\) 給除掉。而同時,因爲球的數量和機器人的數量相同,因此每一個機器人必須認領一個球。class

這乍一看沒啥子用,但放在圖上就是個基環樹森林:test

  • 將圖給構出來:對於每一個球 \((x,y)\),在 \(x\)\(y'\) 間連邊。
  • 因爲每一個機器人必須認領一個球,因此可視做每一個點必須認領一條邊。能夠發現若是這個圖不是基環樹森林,則整個題根本沒法知足。
  • 因爲每一個球須要被一個機器人撿拾,因此須要給基環樹森林的每條邊定向,使得每一個點只有一條入邊(一個點的入邊即它認領的邊,即 「一種合法的定向方案」 對應着 「一種合法的球和機器人的匹配方式」)。
  • 爲了知足題目中所描述的「機器人只會拿離本身最近的球」的限制,對於\(A\)類機器人 \((x,0)\) 去拿球 \((x,y)\),須要\(B\)類機器人將 \((x,t),t\in[1,y)\) 上的球清理乾淨先,因此這些 \(B\) 類 機器人須要比這個 \(A\)類機器人更早被觸發。

原題轉化爲:對於每種給森林定向的方案,求出其知足上述拓撲關係的排列個數。di


先考慮對於某種定向方案,求出合法排列方案。

若是對於這種拓撲關係連邊,最終這些拓撲序關係的邊將組成一個內向樹森林:

  • 若負責拿取球 \((x_1,y_1)\) 機器人 是 負責拿取球 \((x_2,y_2)\) 機器人 的先決條件,則其至少須要知足 \(x_1+y_1<x_2+y_2\),這證明了整個圖應該是一個 \(DAG\)
  • 每一個 \(B\)類機器人只多是一個 \(A\)類機器人的先決條件,因此每一個點只有一條出邊。
  • 綜上:這是一個內向樹森林。

對於內向樹森林求拓撲序的部分,應該都很熟悉了:設森林點集爲 \(S\),節點 \(i\) 的子樹大小爲 \(sz_i\),則這個內向樹森林的拓撲序個數爲

\[\frac {|S|!}{\prod_{x\in S}sz_x}\]

簡要說一下證實:能夠將Dp式列出來,將每一個節點 \(x\) 在自身計算的係數 \((sz_x-1)!\) 與在其父親 \(f\) 處計算的 \(\frac 1{sz_x!}\) 抵消,可得 \(\frac 1{sz_x}\),綜合可得上述公式。森林的話,考慮拿一個虛根將全部樹串起來便可。


解決完了求方案數的任務,再考慮枚舉定向方案。

枚舉全部定向方案確定是不可取的,但幸運的是,這是一個基環樹森林。

對於一棵基環樹而言,其非環邊只能由遠離環的點認領,而環則有順逆兩種方式認領,能夠暴力枚舉。

由幼兒園學習的乘法分配率可知,不一樣的樹之間不必一塊兒枚舉,因此對於每棵樹算出兩種定向方式的答案和,再將全部樹的答案乘起來便可。


時間複雜度明顯是線性,頂可能是枚舉每棵樹的兩種定向方式時有個 \(2\) 的常數

Code

#include <cstdio>
#include <cctype>
typedef long long ll;

template <typename _tp> inline void read(_tp&x){
    char ch=getchar(),ob=0;x=0;
    while(ch!='-'&&!isdigit(ch))ch=getchar();if(ch=='-')ob=1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();if(ob)x=-x;
}

const int N = 201000;
struct Edge {int v, nxt;} a[N*4];
int head[N], Head[N], indeg[N];
int sz[N], pr[N];
bool vis[N];
int n, _;

inline void add(int x, int y, int*arr) {
    a[++_].v = y, a[_].nxt = arr[x], arr[x] = _;
}

const int p = 1e9+7;
int fac[N], ifac[N], inv[N];

int st[N], tp;
#define FID(i) for(int id=1,i;i=st[id],id<=tp;++id)

int X, Y;
int et, pt;

void bfs(int x, int las) {
    vis[st[++tp] = x] = true, ++pt;
    for(int i=head[x];i;++et,i=a[i].nxt)
        if(!vis[a[i].v]) bfs(a[i].v, x);
        else if(a[i].v != las) X = x, Y = a[i].v;
}

void tfs(int x, int las) {
    for(int i=head[x];i;i=a[i].nxt)
        if(a[i].v != las and a[i].v != X)
            pr[a[i].v] = x, tfs(a[i].v, x);
}

void dfs(int x, int las) {
    sz[x] = 1;
    for(int i=Head[x];i;i=a[i].nxt)
        if(a[i].v != las)
            dfs(a[i].v, x), sz[x] += sz[a[i].v];
}

int solve() {
    tfs(X, Y);
    pr[X] = Y;
    FID(i) Head[i] = indeg[i] = 0;
    FID(x) for(int i=head[x];i;i=a[i].nxt)
        if(a[i].v < pr[x]) add(x, a[i].v, Head), ++indeg[a[i].v];
    
    FID(i) if(!indeg[i]) dfs(i, i);
    int Ans = fac[pt];
    FID(i) Ans = (ll)Ans * inv[sz[i]]%p;
    return Ans;
}

int main() {
    read(n);
    for(int i=1,x,y;i<=(n<<1);++i) {
        read(x), read(y), y += n;
        add(x, y, head), add(y, x, head);
    }
    
    n <<= 1;
    fac[0] = fac[1] = inv[0] = inv[1] = ifac[0] = ifac[1] = 1;
    for(int i=2;i<=n;++i) {
        fac[i] = (ll)fac[i-1] * i%p;
        inv[i] = (ll)(p-p/i) * inv[p%i]%p;
        ifac[i] = (ll)ifac[i-1] * inv[i]%p;
    }
    
    int Ans = fac[n];
    for(int i=1;i<=n;++i)
        if(!vis[i]) {
            tp = pt = et = 0;
            bfs(i, i);
            Ans = (ll)Ans * ifac[pt]%p;
            if((pt << 1) != et) return puts("0"), 0;
            int res = solve();
            X ^= Y, Y ^= X, X ^= Y;
            res += solve();
            Ans = (ll)Ans * res%p;
        }
    printf("%d\n", Ans);
    return 0;
}
相關文章
相關標籤/搜索