AGC038 部分題解

E

題意

有一個隨機數生成器,生成 \([0,n-1]\) 之內的數,生成第 \(i\) 個數的概率爲 \(\frac{A_i}{S}\),其中 \(S=\sum_{i=1}^n A_i\)

\(\forall i \in [0,n-1]\) \(i\) 被生成至少 \(B_i\) 次時停止生成,求期望生成次數,對大質數取模。

題解

設第 \(i\) 個數最後一次被隨機到的時間爲 \(t_i\),答案就是 \(\max t_i\) 的期望。

限制所有都要完成一般都不好做,考慮 \(min-max\) 容斥變成枚舉一個集合 \(S\) ,求 \(\min_{i \in S} t_i\) 的期望。

枚舉集合後,問題變成求存在 \(S\) 內某一個元素被隨機 \(b_i\) 次這個事件發生的期望時間。我們發現這樣還是有可能隨機到 \(S\) 外的元素的,一個經典套路是算出期望多少次能操作一次 \(S\) 內的元素,然後轉化爲只考慮 \(S\) 內元素操作的問題,最後乘上去即可。可以發現這個期望次數爲 \(\frac{\sum_{i=1}^n A_i}{\sum_{i \in S} A_i}\)

現在問題在於給定一個集合 \(S\) ,在 \(S\) 內隨機,求存在一個數滿足條件的期望時間。首先根據:

\[\sum_{i \geq 0} P[X=i]i = \sum_{i \geq 0} P[X \geq i] \]

可以得到,我們只需要對於每個時間 \(T\) 求出在 \(T\) 時間還沒結束的概率,求和即可。

我們考慮如何暴力的計算:先枚舉在時間 \(T\) 結束,接下來枚舉序列 \(b_i\) 表示集合中元素 \(i\) 被隨機到的次數,需要滿足 \(\forall i \in S,b_i < B_i\)\(\sum_{i \in S} b_i = T\),這樣一個序列的概率就是 \(\prod_{i \in T} (\frac{A_i}{\sum_{i \in S} A_i})^{b_i}\frac{T!}{\prod_{i \in S} b_i!}\)

所以答案是:

\[\sum_{S \subseteq [n],S \neq \emptyset} (-1)^{|S|-1}\frac{\sum_{i=1}^n A_i}{\sum_{i \in S} A_i} \sum_{T=0}^{\sum_{i \in S} (b_i-1) + 1} \sum_{\{b\},\sum_{i \in S} b_i = T} \prod_{i \in T} (\frac{A_i}{\sum_{i \in S} A_i})^{b_i}\frac{T!}{\prod_{i \in S} b_i!} \]

稍加整理可得

\[\sum_{S \subseteq [n],S \neq \emptyset} (-1)^{|S|-1} \frac{\sum_{i=1}^n A_i}{\sum_{i \in S} A_i} \sum_{T=0}^{\sum_{i \in S} (b_i-1) + 1} T!(\frac{1}{\sum_{i \in S} A_i})^T \sum_{\{b\},\sum_{i \in S} b_i = T} \prod_{i \in S} \frac{A_i^{b_i}}{b_i!} \]

於是可以考慮對這個 dp:發現最終答案係數只和 \(\sum b_i,\sum A_i\) 有關,我們帶着容斥係數 dp,設 \(f_{i,j,k}\) 表示考慮前 \(i\) 個數,\(\sum b_i = j,\sum A_i = k\) 的答案,轉移的時候枚舉這個位置選或者不選,選的話枚舉 \(b_i\) 是多少,可以得到轉移式子:

\[f_{i,j,k} = f_{i-1,j,k}-\sum_{x=0}^{B_i-1}\frac{A_i^x}{x!} f_{i-1,j-A_i,k-x} \]

然後最後把應該乘的係數乘上去就好了。

假設 \(n,\sum A,\sum B\) 同階,看起來時間複雜度是 \(O(n^4)\) 的,但是實際分析一下:複雜度是:

\[\begin{aligned} \sum_{i=1}^n \sum_{j=0}^{\sum A_i} \sum_{k=0}^{\sum B_i } \sum_{l=0}^{B_i-1} 1 &= \sum_{i=1}^n \sum_{l=0}^{B_i-1} (\sum A_i) (\sum B_i)\\ &=(\sum A_i)(\sum B_i)^2 \\ &= O(n^3) \end{aligned} \]

所以可以通過此題。代碼可能和博客定義的是反的,感性理解一下(

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 400+5;
const int ha = 998244353;
int n,a[MAXN],b[MAXN];
int f[MAXN][MAXN];
int sma,smb;

inline void add(int &x,int y){
    x += y-ha;x += x>>31&ha;
}

inline int qpow(int a,int n=ha-2){
	int res = 1;
	while(n){
		if(n & 1) res = 1ll*res*a%ha;
		a = 1ll*a*a%ha;
		n >>= 1;
	}
	return res;
}

int fac[MAXN],inv[MAXN];

int main(){
    fac[0] = 1;FOR(i,1,MAXN-1) fac[i] = 1ll*fac[i-1]*i%ha;
    inv[MAXN-1] = qpow(fac[MAXN-1]);ROF(i,MAXN-2,0) inv[i] = 1ll*inv[i+1]*(i+1)%ha;
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d%d",a+i,b+i);
    FOR(i,1,n) sma += a[i],smb += b[i]-1;
    f[0][0] = -1;
    FOR(i,1,n){
        ROF(j,sma,a[i]){
            ROF(k,smb,0){
                ROF(x,std::min(k,b[i]-1),0){
                    add(f[j][k],(ha-1ll*f[j-a[i]][k-x]*inv[x]%ha*qpow(a[i],x)%ha)%ha);
                }
            }
        }
    }
    int ans = 0;
    FOR(j,0,sma) FOR(k,0,smb){
        int gx = f[j][k];
        gx = 1ll*gx*fac[k]%ha*sma%ha;
        gx = 1ll*gx*qpow(qpow(j,k+1))%ha;
        add(ans,gx);
    }
    printf("%d\n",ans);
    return 0;
}

F

題意

給定兩個長度爲 \(n\) 的排列 \(P,Q\),要求你構造兩個長度爲 \(n\) 的排列 \(A,B\),滿足:

  • \(A_i\) 取值 \(\{i,P_i\}\)
  • \(B_i\) 取值 \(\{i,Q_i\}\)

要求 \(A,B\) 不同的位置儘量多,求出這個最多的位置個數。

題解

\(P,Q\) 分成若干個環,同一個環要麼都取 \(i\) 要麼都取 \(P_i/Q_i\)

討論一下 \(P_i,Q_i\) 之間的關係,可以得到以下情況:

  • \(P_i=Q_i=i\):無論如何都相等。
  • \(P_i,Q_i \neq i,P_i \neq Q_i\):同時不轉相等。
  • \(P_i=i,Q_i \neq i\)\(B_i\) 不轉相等。
  • \(P_i \neq i,Q_i = i\)\(A_i\) 不轉相等。
  • \(P_i=Q_i,P_i,Q_i \neq i\):同時轉相等。

網絡流。把 \(B\) 倒過來就行了。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 2e5 + 5;

struct Edge{
    int to,w,nxt;
}e[MAXN<<4];
int head[MAXN],cnt=1,cur[MAXN],dep[MAXN];

inline void add(int u,int v,int w){
    e[++cnt] = (Edge){v,w,head[u]};head[u] = cnt;
    e[++cnt] = (Edge){u,0,head[v]};head[v] = cnt;
}

int S,T,N;

inline bool bfs(){
    FOR(i,0,N) cur[i] = head[i],dep[i] = 0;
    std::queue<int> q;q.push(S);dep[S] = 1;
    while(!q.empty()){
        int v = q.front();q.pop();
        for(int i = head[v];i;i = e[i].nxt){
            if(e[i].w > 0 && !dep[e[i].to]){
                dep[e[i].to] = dep[v]+1;
                q.push(e[i].to);
            }
        }
    }
    return dep[T];
}

inline int dfs(int v,int lim=1e9){
    if(v == T) return lim;
    if(!lim) return 0;
    int ans = 0;
    for(int &i = cur[v];i;i = e[i].nxt){
        if(e[i].w > 0 && dep[e[i].to] == dep[v]+1){
            int t = dfs(e[i].to,std::min(lim,e[i].w));
            if(t > 0){
                ans += t;
                e[i].w -= t;
                e[i^1].w += t;
                lim -= t;
                if(!lim) break;
            }
        }
    }
    return ans;
}

inline int Dinic(){
    int ans = 0,flow;
    while(bfs()) while((flow=dfs(S))) ans += flow;
    return ans;
}

int n,p[MAXN],q[MAXN];
bool vis[MAXN];
int belp[MAXN],belq[MAXN];

inline void dfs(int v,int now,int p[],int bel[]){
    vis[v] = 1;bel[v] = now;
    if(vis[p[v]]) return;
    dfs(p[v],now,p,bel);
}

int main(){
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d",p+i),++p[i];
    FOR(i,1,n) scanf("%d",q+i),++q[i];
    FOR(i,1,n) if(!vis[i]) ++N,dfs(i,N,p,belp);
    FOR(i,1,n) vis[i] = 0;
    FOR(i,1,n) if(!vis[i]) ++N,dfs(i,N,q,belq);
    S = N+1;T = N+2;N = T;
    int ans = n;
    FOR(i,1,n){
        if(p[i] == q[i]){
            if(p[i] == i){
                ans--;continue;
            }
            else{
                add(belp[i],belq[i],1);
                add(belq[i],belp[i],1);
            }
        }
        else{
            if(p[i] == i && q[i] != i){
                add(belq[i],T,1);
            }
            else if(p[i] != i && q[i] == i){
                add(S,belp[i],1);
            }
            else{
                add(belq[i],belp[i],1);
            }
        }
    }
    printf("%d\n",ans-Dinic());
    return 0;
}
/*
p: 割左邊=不轉 右邊=轉
q反過來 
何時答案會-1:
p[i]=q[i]=i 無論如何都會-1
p[i],q[i] != i 同時不轉
p[i]=i,q[i]!=i 2不轉
p[i]!=i,q[i]=i 1不轉
p[i]=q[i],p[i],q[i]!=i  同時轉
*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章