1. 程式人生 > >題解-AtCoder Code-Festival2017qualA-E Modern Painting

題解-AtCoder Code-Festival2017qualA-E Modern Painting

c++ == 方案 左右 輸出 分享圖片 第一個 對稱 之一

Problem

CODE-FESTIVAL 2017 qual A

洛谷賬戶的提交通道

題意:有一個\(n\)\(m\)列的方格,在邊界外有可能有機器人(坐標為\((0,x),(n+1,x),(x,0),(x,m+1)\),機器人方向朝內(也就是左邊界外的機器人朝右,上邊界外的機器人朝內……),機器人有自己獨特的顏色(沒有兩個機器人顏色相同)。

給機器人安排一個出發順序,機器人將依次出發,對於每個機器人,走過的路上會留下自己的顏色,它將繼續前進除非出了另一方的邊界或遇到了其他機器人留下的顏色。求有多少種最終狀態(即方格染色情況不同)

\(n,m\leq 10^5\)

輸入是給出每個邊界外是否有機器人,即機器人數量不超過\(2(n+m)\leq 4\times 10^5\)

Solution

想了半天未果,然後看到題解的圖恍然大悟

情況太復雜,考慮簡化版的情況:先考慮只有一個角或半平面的情況


若棋盤只有一個角(角塊)

可假定機器人僅從上、左出發,設有\(n\)\(m\)列,共\(n+m\)個機器人

先畫出一個\(n\)\(m\)列的方格,共有\(nm\)個交點,隨意畫出一種結果,發現如果始終沿著最內側線走,並在最下邊和最右邊加限制邊,可以得到下面這張圖(圖中紅色路徑):

技術分享圖片

此時發現其中的紅色路徑依次穿過了\(n+m\)條直線(盡管有的沒和線段相交,但和延長線相交),而實際上整張圖最終的狀態也只會和這個穿過的順序有關

由於穿過\(n\)條橫線的順序是固定的,穿過\(m\)

條橫線的順序也是固定的,所以這個狀態數相當於找\(n\)個黑球和\(m\)個白球排成一列的不同方案數,由組合知識可知方案數為\(\binom {n+m}n\)


若棋盤只有一個半平面

可假定機器人從上、左、右出發

利用上面的思想:若第一個機器人是上下移動的,則會將這個狀態化為一個子問題;若第一個機器人是從左往右的,則會將這個狀態劃分為兩個角的情況(也就是上面說的)

這樣就能簡單地推廣到全平面問題了:第一個機器人將局面劃分為兩個半平面

然後經過思考得知,這樣復雜度為\(O(nm)\),雖然通過不了此題,但若是在\(OI\)賽制的考試中,就能與別人拉開巨大的差距


對稱思想

上面的做法是通過不了此題的,思考半天也沒發現可以利用組合數公式優化 然後就翻開了罪惡的題解……

先放一張題解的圖

技術分享圖片

之前在四分之一角那一塊中提到的紅線,在官方題解裏它是一個紅色區域

  • 在角中紅色區域是一個單調的階梯狀是因為只有上面和左邊的機器人
  • 考慮到在半平面情況中,還存在下邊的機器人,所以紅色區域為一個單峰的階梯狀

如果利用之前角塊的思想解半平面,可以利用圖中所給的對稱軸,將單峰的階梯對稱為單調的階梯……然後就能用角塊的公式來解半平面了!

列下公式:若左邊有\(n\)個機器人,上面和下面共有\(m\)個機器人,則答案為\(\binom {n+m}n-\binom {n+m-1}n=\binom {n+m-1}{n-1}\)(之所以要減去是因為至少要沿著對稱軸走一次才能保證對稱的正確性)


匯總

然後就可以枚舉第一只機器人如何劃開平面將其劃分為半平面的,假設第一只機器人是走豎直線段

相應的,由於不好處理半平面中“一只機器人走完仍然是一個半平面”的問題,所以可以枚舉不會出現這種狀態的左右邊界,然後這個枚舉可以用前綴和優化成\(O(n)\)


特判

  • 註意第一只機器人的走向可能是橫向,也可能是縱向
  • 註意組合數部分不要爆負
  • 註意若同一列(行)若有上下兩個機器人,且這兩個機器人之間沒有障礙,需要將答案乘以2(因為可以上面先走或下面先走)
  • 若根本沒有機器人,應該輸出1

Code

再不懂就看代碼吧

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=101009,p=998244353,inv2=p+1>>1;
char A[N],B[N],C[N],D[N];
int fac[N<<2],inv[N<<2];
int n,m,ans;

inline int qpow(int A,int B){
    int res(1);while(B){
        if(B&1)res=(ll)res*A%p;
        A=(ll)A*A%p,B>>=1;
    }return res;
}

inline int Com(int nn,int mm){return (ll)fac[nn]*inv[mm]%p*inv[nn-mm]%p;}
void prework(int A){
    fac[0]=inv[0]=1;
    for(int i=1;i<=A;++i)fac[i]=(ll)fac[i-1]*i%p;
    inv[A]=qpow(fac[A],p-2);
    for(int i=A-1;i;--i)inv[i]=(ll)inv[i+1]*(i+1)%p;
}

void work(){
    int left=0,right=0,l=0,r=0,pre=1,val;
    for(int i=1;i<=n;++i)left+=A[i]-‘0‘,right+=B[i]-‘0‘;
    for(int i=m;i;--i)r+=C[i]-‘0‘+D[i]-‘0‘;
    for(int i=1;i<=m;i++){
        l+=C[i]-‘0‘+D[i]-‘0‘;
        r-=C[i]-‘0‘+D[i]-‘0‘;
        if(C[i]==‘0‘ and D[i]==‘0‘)continue;
        if(C[i]==‘1‘ and D[i]==‘1‘)pre=2ll*pre%p;
        if(right)val=Com(right+r-1,r);
        else val=(!r);      ans=(ans+(ll)pre*val)%p;
        if(left)val=Com(left+l-1,l);
        else val=(!l);      pre=(pre+val)%p;
    }
}

int main(){
    scanf("%d%d",&n,&m);
    prework(n+m+n+m);
    scanf("%s%s",A+1,B+1);
    scanf("%s%s",C+1,D+1);
    bool died=true;
    for(int i=1;i<=n;++i)
        if(A[i]==‘1‘ or B[i]==‘1‘ or C[i]==‘1‘ or D[i]==‘1‘)
            {died=false;break;}
    if(died){puts("1");return 0;}
    work();
    swap(n,m);swap(A,C);
    swap(B,D);swap(C,D);
    work();
    printf("%d\n",ans);
    return 0;
}

題解-AtCoder Code-Festival2017qualA-E Modern Painting