1. 程式人生 > 其它 >[atARC128F]Game against Robot

[atARC128F]Game against Robot

為了方便,下文中的$n$是原來的$\frac{n}{2}$

當確定排列$\{p_{i}\}$後,將$a_{i}$按照$p_{i}$從大到小排序,那麼機器人即會不斷選第一個元素

考慮玩家最後選擇的$n$個元素,合法當且僅當$\forall 1\le i\le n,$其在前$2i$個元素至多選$i$個元素

必要性:考慮前$i$輪,機器人選的總在前$2i$個元素中,反之也即玩家至多在前$2i$個元素中選$i$個元素

充分性:每一輪,玩家不斷選擇第一個要選的元素

若這樣的策略不合法,必然是某次機器人選了玩家要選的元素,假設第一次出現此情況是第$i$輪,由策略該元素前恰有$i$個玩家要選的元素和$i-1$個玩家不選的元素,也即前$2i$個元素中有$i+1$個元素,矛盾

反之也即等價於在後$2i$個元素中,至少選$i$個元素,考慮下述過程:

維護可重集$S$(初始為空),從大到小列舉$i\in [1,n]$,將$a_{2i-1},a_{2i}$加入$S$,並取出$S$中最大的元素(至少要再額外選一個),那麼$n$次所取出的元素和即是答案

回到原問題,也即對$\{a_{i}\}$的所有排列求上述過程的答案和

將答案轉化為$\sum_{x\in Z^{+}}$取出元素$\ge x$的次數,而對於一個確定的$x$,將元素按照是否$\ge x$標記為01,此時僅需考慮一個01序列的答案(取1的次數)

記$cnt_{i}$為後$2i$個位置中1的個數,則答案為$\min_{0\le i\le n}(cnt_{i}+n-i)$

對此進行歸納——

考慮第$i$次取元素時,$S$中已加入了$cnt_{i}$個1、取出了$\min_{0\le j<i}(cnt_{j}+(i-1)-j)$個1(記後者為$s$),顯然取出1當且僅當$[cnt_{i}>s]$,進而(新)答案即$s+[cnt_{i}>s]$

代入式子,也即求證$\min_{0\le j\le i}(cnt_{j}+i-j)=s+[cnt_{i}>s]$,兩者均可轉換為$\min(s+1,cnt_{i})$,顯然相等,即得證

交換排列和$x$的列舉順序,問題即變為:求所有長為$2n$且恰有$k$個1的01序列上述答案之和

(其中$k$為$\ge x$的元素個數,由於01內部也是不同的,最後還要乘上$k!(2n-k)!$)

為了方便處理,對問題做以下變形——

將序列翻轉並將0變為-1,記$sum_{i}$為字首和,則答案為$n+\frac{\min_{0\le i\le n}sum_{i}}{2}$

$n$可以直接統計,後者轉化為$-\sum_{x_{0}\in Z^{-}}[\min_{0\le i\le n}sum_{2i}\le 2x_{0}]$,並交換序列和$x_{0}$的列舉順序

分析奇偶性,若$sum_{2i+1}\le 2x_{0}$則$sum_{2i}\le 2x_{0}$,進而不妨轉換為$\min_{0\le i\le 2n}sum_{i}\le 2x_{0}$

簡單構造,問題即統計從$(0,0)$到$(2n,2k-2n)$,每一步$x$座標+1、$y$座標$\pm 1$且與$x=2x_{0}$有公共點的路徑數

這是一個經典問題,通過翻轉可得答案為$\begin{cases}{2n\choose k}&(x_{0}\ge k-n)\\{2n\choose k-2x_{0}}&(x_{0}<k-n)\end{cases}$

將其累加,總答案為$\begin{cases}\sum_{k<i\le 2n,i\equiv k(mod\ 2)}{2n\choose i}&(k-n\ge 0)\\(n-k){2n\choose k}+\sum_{2n-k<i\le 2n,i\equiv k(mod\ 2)}{2n\choose i}&(k-n<0)\end{cases}$

兩者都可以通過簡單預處理快速求出,同時$x$的列舉僅需要考慮$x=a_{i}$的取值

時間複雜度為$o(n)$,可以通過

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000005
 4 #define mod 998244353
 5 #define ll long long
 6 int n,m,ans,a[N],fac[N],inv[N],sum[N];
 7 int C(int n,int m){
 8     return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod;
 9 }
10 int calc(int k){
11     int ans=(ll)n*C(m,k)%mod;
12     if (k>=n)ans=(ans-sum[k+2]+mod)%mod;
13     else ans=(ans-((ll)(n-k)*C(m,k)+sum[m-k+2])%mod+mod)%mod;
14     return (ll)ans*fac[k]%mod*fac[m-k]%mod;
15 }
16 int main(){
17     fac[0]=inv[0]=inv[1]=1;
18     for(int i=1;i<N;i++)fac[i]=(ll)fac[i-1]*i%mod;
19     for(int i=2;i<N;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
20     for(int i=1;i<N;i++)inv[i]=(ll)inv[i-1]*inv[i]%mod;
21     scanf("%d",&m),n=(m>>1);
22     for(int i=1;i<=m;i++)scanf("%d",&a[i]);
23     sort(a+1,a+m+1);
24     for(int i=m;i;i--)sum[i]=(sum[i+2]+C(m,i))%mod;
25     for(int i=1;i<=m;i++)
26         if ((i==1)||(a[i]!=a[i-1]))ans=(ans+(ll)(a[i]-a[i-1])*calc(m-i+1))%mod;
27     printf("%d\n",ans);
28     return 0;
29 }
View Code