1. 程式人生 > 其它 >2021牛客暑期多校訓練營9 部分題題解

2021牛客暑期多校訓練營9 部分題題解

C.Cells

題目連結

Cells

簡要題解

這個題首先需要用到LGV引理,引理的具體內容此處不加討論。
我們根據LGV引理得到,所要求的答案就是下面那個行列式:

\[\left| \begin{array}{cccc} C_{a_1+1}^1 & C_{a_1+2}^2 & \cdots & C_{a_1+n}^n \\ C_{a_2+1}^1 & C_{a_2+2}^2 & \cdots & C_{a_2+n}^n \\ \vdots & \vdots & \ddots & \vdots \\ C_{a_n+1}^1 & C_{a_n+2}^2 & \cdots & C_{a_n+n}^n \\ \end{array} \right| \]

樸素的行列式求值是利用高斯消元,但是顯然在這裡不適用,於是我們要進行更多的推導。
進行一步轉置操作,答案不變。

\[\left| \begin{array}{cccc} C_{a_1+1}^1 & C_{a_1+2}^2 & \cdots & C_{a_1+n}^n \\ C_{a_2+1}^1 & C_{a_2+2}^2 & \cdots & C_{a_2+n}^n \\ \vdots & \vdots & \ddots & \vdots \\ C_{a_n+1}^1 & C_{a_n+2}^2 & \cdots & C_{a_n+n}^n \\ \end{array} \right|= \left| \begin{array}{cccc} C_{a_1+1}^1 & C_{a_2+1}^1 & \cdots & C_{a_n+1}^1 \\ C_{a_1+2}^1 & C_{a_2+2}^2 & \cdots & C_{a_n+2}^2 \\ \vdots & \vdots & \ddots & \vdots \\ C_{a_1+n}^n & C_{a_2+n}^n & \cdots & C_{a_n+n}^n \\ \end{array} \right| \]

我們拆掉組合數,把分母的階乘提出來,得到

\[\prod_{i=1}^n\frac{1}{i!}\left| \begin{array}{cccc} \frac{(a_1+1)!}{a_1!} & \frac{(a_2+1)!}{a_2!} & \cdots & \frac{(a_n+1)!}{a_n!} \\ \frac{(a_1+2)!}{a_1!} & \frac{(a_2+2)!}{a_2!} & \cdots & \frac{(a_n+2)!}{a_n!} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{(a_1+n)!}{a_1!} & \frac{(a_2+n)!}{a_2!} & \cdots & \frac{(a_n+n)!}{a_n!} \\ \end{array} \right| \]

我們對這個行列式進行初等行變換,那麼我們可以得到

\[\prod_{i=1}^n\frac{a_i+1}{i!}\left| \begin{array}{cccc} 1 & 1 & \cdots & 1 \\ (a_1+1) & (a_2+1) & \cdots & (a_n+1) \\ \vdots & \vdots & \ddots & \vdots \\ (a_1+1)^{n-1} & (a_2+1)^{n-1} & \cdots & (a_n+1)^{n-1} \\ \end{array} \right| \]

具體怎麼進行初等行變換呢,拿三行一列來舉個例子:

\[\left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)*(a_1+2)*(a_1+3)\\ \end{array} \right|\Rightarrow \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)^2*(a_1+2)\\ \end{array} \right|\Rightarrow \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)^2 \\ (a_1+1)^2*(a_1+2)\\ \end{array} \right|\Rightarrow \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)^2 \\ (a_1+1)^3\\ \end{array} \right| \]

右邊那個是範德蒙行列式,它的值$$D_n=\prod_{1\leq j<i\leq n}((a_i+1)-(a_j+1))$$
這個東西可以構造多項式求解,我們令\(f(x)=\sum_{i=1}^{n}x^{a_i}\)\(g(x)=\sum_{i=1}^nx^{-a_i}\)
\(NTT\)\(f(x)\)\(g(x)\)做卷積,那麼\(x\)的指數就是\(a_i-a_j\),係數就是滿足\(a_i-a_j\)\((i,j)\)對數。
\(h(x)=f(x)*g(x)=\sum_{i=1}^{\infty}H_i*x^i\),那麼\(D_n=\prod_{i=1}^{\infty}i^{H_i}\)
我們對\(D_n\)取模時,講道理\(H_i\)要對\(998244352\)取模,但是觀察發現\(H_i\)不可能超過模數,所以可以直接\(NTT\)
時間複雜度\(O(n*logn)\)
程式碼如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
const int Mod=998244353;
int n,m,Ans,Seq[MAXN],Fac[MAXN],Inv[MAXN];
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&(b<'0'||b>'9')) b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c;
}
int Pow(int Base,int P)
{   int Ret=1,Cnt=Base;
    for(;P>=1;P>>=1) P&1?Ret=1ll*Ret*Cnt%Mod:0,Cnt=1ll*Cnt*Cnt%Mod;
    return Ret;
}
int Add(int A,int B){   return A+=B,A>=Mod?A-Mod:A;   }
namespace Poly
{   int Len,Ms,Inv,Rader[1<<21],A[1<<21],B[1<<21];
    void NTT(int *P,int K)
    {   for(int i=0;i<Len;i++)
            if(i<Rader[i]) swap(P[i],P[Rader[i]]);
        for(int i=1;i<Len;i<<=1)
        {   int Euler=Pow(3,(Mod-1)/(i<<1));
            if(K<0) Euler=Pow(Euler,Mod-2);
            for(int Pos=(i<<1),j=0;j<Len;j+=Pos)
            {   int Wi=1;
                for(int k=0;k<i;k++,Wi=1ll*Wi*Euler%Mod)
                {   int X=P[j+k],Y=1ll*Wi*P[i+j+k]%Mod;
                    P[j+k]=Add(X,Y),P[i+j+k]=Add(X,Mod-Y);
                }
            }
        }
        if(K>0) return ;
        Inv=Pow(Len,Mod-2);
        for(int i=0;i<Len;i++) P[i]=1ll*P[i]*Inv%Mod;
    }
    void Prepare(int Lx)
    {   Ms=-1;
        for(Len=1;Len<=Lx;Len<<=1) Ms++;
        for(int i=0;i<Len;i++) Rader[i]=(Rader[i>>1]>>1)|((i&1)<<Ms);
    }
    void Work()
    {   for(int i=1;i<=n;i++) A[Seq[i]]++,B[1000001-Seq[i]]++;
        Prepare(2e6),NTT(A,1),NTT(B,1);
        for(int i=0;i<Len;i++) A[i]=1ll*A[i]*B[i]%Mod;
        NTT(A,-1);
    }
}using Poly::Work;using Poly::A;
int main()
{   n=Read(),Fac[0]=Ans=1;
    for(int i=1;i<=n;i++) Seq[i]=Read(),Fac[i]=1ll*Fac[i-1]*i%Mod;
    Inv[n]=Pow(Fac[n],Mod-2);
    for(int i=n-1;i>=0;i--) Inv[i]=1ll*Inv[i+1]*(i+1)%Mod;
    for(int i=1;i<=n;i++) Ans=1ll*Ans*(Seq[i]+1)%Mod*Inv[i]%Mod;
    Work();
    for(int i=1000002;i<=2000001;i++) Ans=1ll*Ans*Pow(i-1000001,A[i])%Mod;
    printf("%d\n",Ans);
}

I.Incentive Model

題目連結

Incentive Model

簡要題解

題目的大意是,\(A\)\(B\)兩個人依次爭奪\(n\)塊區域,兩人分別有個初始能力值\(a\)\(b\)
對於每一塊區域的爭奪,\(A\)獲勝的概率為\(\frac{a}{a+b}\)\(B\)獲勝的概率為\(\frac{b}{a+b}\),獲勝的一方將會增加\(w\)點能力值。
求最終\(A\)獲勝的期望次數。

考場上大部分人沒讀懂題,這個可以直接做。
我們設\(E(x)\)表示,已經爭奪了\(x\)塊區域,\(A\)的期望能力值,那麼不難寫出轉移方程:

\[E(x+1)=E(x)+\frac{E(x)}{1+w*x}w \]\[E(x+1)=\frac{1+(w+1)*x}{1+w*x}E(x) \]\[\frac{E(x+1)}{1+(w+1)*x}=\frac{E(x)}{1+w*x} \]

於是我們得到

\[\frac{E(x)}{1+w*x}=\frac{E(0)}{1}=a \]\[E(x)=a+a*x*w \]

由於總能力值等於初始能力值加上後面獲勝取得的能力值,我們令\(x=n\),就能發現$$Ans=a*n$$
直接輸出即可。
程式碼如下:

#include<bits/stdc++.h>
using namespace std;
const int Mod=998244353;
int n,W,X,Y;
int Pow(int B,int P)
{   int Ret=1,Cnt=B;
    while(P>=1) P&1?Ret=1ll*Ret*Cnt%Mod:0,Cnt=1ll*Cnt*Cnt%Mod,P>>=1;
    return Ret;
}
int main()
{   scanf("%d%d%d%d",&n,&W,&X,&Y);
    printf("%lld\n",1ll*X*Pow(Y,Mod-2)%Mod*n%Mod);
}

J.Jam

題目連結

Jam

簡要題解

由於道路不是很多,我們可以手動把情況全部枚舉出來。
可以發現,對於右轉的車輛,不會與任何車道的車衝突,於是右轉的燈可以一直亮著。
剩下了\(12\)條車道,我們經過手玩可以發現,每一秒鐘,最多同時亮兩個綠燈,我們還可以求出能夠在同一秒亮綠燈的所有車道對。
既然要時間最短,我們肯定希望同時亮兩個綠燈的次數最多,這就變成了一個求最大匹配的問題。
解決方法比較多,這裡介紹一種網路流建圖跑最大流的做法。

既然要求匹配,我們就把每個車道拆成兩個點,放在左右兩排,還有源點\(S\)和匯點\(T\)
左邊一排點和\(S\)連邊,右邊一排點和\(T\)連邊,流量為對應車道的車輛數。
兩排點之間,能夠匹配的就連邊,注意同一車道的兩個點不能連邊,因為這不是一組合法的匹配。
跑出來的最大流除以\(2\),就是最大匹配數了。

為什麼要除以\(2\)?我們建圖跑出來的最大流並不是真正的匹配數,一個車道對應了兩個點。
對於一組匹配\((a,b)\)來說,可以是左邊的\(a\)匹配右邊的\(b\),也可以是右邊的\(a\)匹配左邊的\(b\)
但是我們得到的是最大流,感性理解,最優方案一定是最優匹配正反各做一次。
換句話說,如果在真正的最大匹配中,\((a,b)\)是其中的一組匹配,那麼在我們建的圖中,左右匹配兩次肯定最優。

程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int Ts,Ans,Car[200],Cp[200][3];
int Seq[200]={2,3,7,8,9,12,13,14};
void Init()
{   Cp[2][0]=3,Cp[2][1]=12,Cp[2][2]=14;
    Cp[3][0]=2,Cp[3][1]=7,Cp[3][2]=9;
    Cp[7][0]=3,Cp[7][1]=8,Cp[7][2]=13;
    Cp[8][0]=7,Cp[8][1]=12,Cp[8][2]=14;
    Cp[9][0]=3,Cp[9][1]=12,Cp[9][2]=13;
    Cp[12][0]=2,Cp[12][1]=8,Cp[12][2]=9;
    Cp[13][0]=7,Cp[13][1]=9,Cp[13][2]=14;
    Cp[14][0]=2,Cp[14][1]=8,Cp[14][2]=13;
}
int Max(int A,int B){   return A>B?A:B;   }
int Min(int A,int B){   return A<B?A:B;   }
namespace Net
{   struct EDGE{   int u,v,w,Next;   }Edge[100010];
    int S,T,Es,First[100],Deep[100],Vis[100],Cur[100];
    queue<int>Team;
    void Link(int u,int v,int w){   Edge[++Es]=(EDGE){u,v,w,First[u]},First[u]=Es;   }
    int Bfs()
    {   for(int i=S;i<=T;i++) Vis[i]=Deep[i]=0;
        Team.push(S),Deep[S]=1,Vis[S]=1;
        while(!Team.empty())
        {   int k=Team.front();   Team.pop();
            for(int i=First[k];i!=-1;i=Edge[i].Next)
            {   int v=Edge[i].v;
                if(Vis[v]||!Edge[i].w) continue ;
                Vis[v]=1,Deep[v]=Deep[k]+1,Team.push(v);
             }
        }
        return Deep[T];
    }
    int Dinic(int Pos,int Mins)
    {   if(Pos==T) return Mins;
        for(int &i=Cur[Pos];i!=-1;i=Edge[i].Next)
        {   int v=Edge[i].v;
            if(Deep[v]!=Deep[Pos]+1||!Edge[i].w) continue ;
            int X=Dinic(v,Min(Mins,Edge[i].w));
            if(X) return Edge[i].w-=X,Edge[i^1].w+=X,X;
        }
        return 0;
    }
    void Work()
    {   while(Bfs())
        {   for(int i=S;i<=T;i++) Cur[i]=First[i];
            while(int Nv=Dinic(S,1e9)) Ans-=Nv;
        }
    }
    void Build()
    {   Es=-1,S=0,T=33;
        for(int i=0;i<=T;i++) First[i]=-1;
        for(int i=0;i<=7;i++)
        {   Link(S,Seq[i],Car[Seq[i]]),Link(Seq[i],S,0);
            Link(Seq[i]+16,T,Car[Seq[i]]),Link(T,Seq[i]+16,0);
            for(int j=0;j<=2;j++)
            {   int u=Seq[i],v=Cp[u][j];
                Link(u,v+16,1e9),Link(v+16,u,0);
            }
        }
    }
}using namespace Net;
int main()
{   Init();
    for(scanf("%d",&Ts);Ts;Ts--)
    {   for(int i=1;i<=16;i++) scanf("%d",&Car[i]);
        Build(),Work(),Ans/=2;
        for(int i=0;i<=7;i++) Ans+=Car[Seq[i]];
        printf("%d\n",Max(Ans,Max(Car[4],Max(Car[5],Max(Car[10],Car[15]))))),Ans=0;
    }
}