1. 程式人生 > 其它 >ARC 128 F Game against Robot 題解

ARC 128 F Game against Robot 題解

ARC 128 F Game against Robot 題解

一個很有意思的計數題。

首先觀察給定序列能選擇的牌需要滿足什麼性質。

很顯然可以轉變成網路流的模型,必須滿足前\(2k\)個選擇的個數不能超過\(k\)張牌。

考慮每張牌的貢獻,假設當前為\(x\),可以將\(\geq x\)的看作1,\(<x\)的看作0 。

然後就轉變成了\(0-1\)問題。

可以統計所有可能排列選擇的\(1\)的和,也就是選擇比\(x\)大的牌的總數。

可以將\(0\)看成\(-1\),pre為字首和。

然後統計形如:

\[\sum_{pre}\lfloor \max(pre[1],pre[2],pre[3]...pre[n])\rfloor\\ =\sum_{z\geq 1} \sum_{pre}[\exists pre_i\geq 2z] \]

後面那個式子可以看作網格上移動需要觸碰一條\(y-x=2z\)

的直線。

是一個很經典的組合數。

時間複雜度為\(O(N)\)

code:

#include<bits/stdc++.h>
#include<atcoder/all>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define rep(a,b) for(int a=0;a<b;++a)
#define LL long long
#define PB push_back
#define POB pop_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
using namespace atcoder;
using mint = modint998244353;
const int MAXN=1e6+1;
mint fact[MAXN],ifact[MAXN];
mint comb(int A,int B){
    return fact[A]*ifact[B]*ifact[A-B];
}
int a[MAXN],n;
mint cnt[MAXN];
mint tmp[MAXN];
int main(){
    fact[0]=1;
    rb(i,1,1000000) fact[i]=fact[i-1]*i;
    ifact[1000000]=1/fact[1000000];
    rl(i,1000000-1,0) ifact[i]=ifact[i+1]*(i+1);
    scanf("%d",&n);
    rb(i,1,n) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    reverse(a+1,a+1+n);
    tmp[0]=1;
    mint ans=0;
    rb(k,1,n){
        if(k>=2) tmp[k]=tmp[k-2];
        tmp[k]+=comb(n,k);
        cnt[k]=comb(n,k)*k;
        int z=max(2,2*k-n+1);
        if(z%2==1) z++;
		if(k-z>=0)
        cnt[k]-=tmp[k-z];
        z=(z-2)/2;
        cnt[k]-=comb(n,k)*z;
        cnt[k]*=fact[k];
        cnt[k]*=fact[n-k];
        ans+=(cnt[k]-cnt[k-1])*a[k];
//        cout<<k<<" "<<cnt[k].val()<<" "<<endl;
    }
    cout<<ans.val()<<endl;
    return 0;
}