ABC380題解(F&G)

adam01發表於2024-11-16

ABC380F. Exchange Game

因為 \(n+m+k\leq 12\),考慮狀壓 dp,設 \(f(x,s1,s2,s3)\) 表示 先手,後手,桌子上的牌分別是哪一些,這有 \(O(3^n)\) 種狀態。

然後只要列舉出哪一張即可,有

  • \(f(s1,s2,s3)\to f(s2,s1-i+j,s3+i-j)(i\in s1,j\in s3,a_j<a_i)\)
  • \(f(s1,s2,s3)\to f(s2,s1-i,s3+i)(i\in s1)\)

其中 \(x\to y\) 表示 \(y:=y\operatorname{or}\lnot x\)

如果直接 map 記錄,複雜度 \(O(n^33^n)\),可以過但是不夠快。

只要預處理出 \(s=(S)_2\) 在三進製表示的值 \(h_s=(S)_3\) 即可。

那麼 \(f(s1,s2,s3)=g(h_{s2}+2h_{s3})\),複雜度 \(O(n^23^n)\)

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

const int N = 12, M = 6e5;
int a[N], n;

int f[M], pw[N], ss[1 << N];
inline int get(int s1, int s2, int s3) { return ss[s2] + ss[s3] * 2; }

bool dfs(int s1, int s2, int s3)
{
    if(!s1) return 0;
    int S = get(s1, s2, s3);
    if(f[S] != -1) return f[S];
    bool ok = 0;
    for(int i = 0; i < n; i ++)
        if((s1 >> i) & 1)
        {
            for(int j = 0; j < n; j ++)
                if(a[j] < a[i] && ((s3 >> j) & 1))
                    ok |= !dfs(s2, s1 ^ (1 << i) ^ (1 << j), s3 ^ (1 << i) ^ (1 << j));
            ok |= !dfs(s2, s1 ^ (1 << i), s3 ^ (1 << i));
        }
    return f[S] = ok;
}

signed main()
{
    pw[0] = 1; for(int i = 1; i < N; i ++) pw[i] = pw[i - 1] * 3;
    for(int i = 0; i < (1 << N); i ++)
        for(int j = 0; j < N; j ++)
            ss[i] += pw[j] * ((i >> j) & 1);
    ios::sync_with_stdio(0);cin.tie(0);
    int x, y, z; cin >> x >> y >> z;
    n = x + y + z;
    for(int i = 0; i < n; i ++) cin >> a[i];
    int s1 = 0, s2 = 0, s3 = 0;
    s1 = (1 << x) - 1;
    s2 = (1 << y) - 1;
    s3 = (1 << z) - 1;
    s2 <<= x;
    s3 <<= x + y;
    memset(f, -1, sizeof f);
    if(dfs(s1, s2, s3))
        cout << "Takahashi";
    else cout << "Aoki";

    return 0;
}

ABC380G. Another Shuffle Window

考慮怎麼快速計算任意 \(i\) 下的逆序對期望。把排列分割成 \([1,i)[i,i+k)[i+k,n]\) 三段。記為 \([1][2][3]\),把整個排列記為 \(p\)

答案即為 \(E(1,1)+E(1,2)+E(1,3)+E(2,3)+E(3,3)+Ek\)

其中 \(E(x,y)\) 表示 \(x\)\(y\) 構成的逆序對數,\(Ek\) 表示長為 \(k\) 的排列的期望逆序對數,有 \(Ek=\dfrac{k(k-1)}{4}\)

考慮稍微容斥一下:

\[\begin{aligned} Ans&=(E(1,1)+E(1,2)+E(1,3))+(E(1,3)+E(2,3)+E(3,3))-E(1,3)+Ek\\ &=E(1,p)+E(p,3)+Ek-E(1,3) \end{aligned} \]

然後預處理 \(2n\)\(E(p,x),E(x,p)\),現在的問題是求 \(E(1,3)\)

發現在從左到右增加 \(i\) 的過程中,可以透過兩個樹狀陣列維護 \([1][3]\) 裡的值,然後直接做查詢即可。

複雜度 \(O(n\log n)\)

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

const int N = 2e5 + 5, p = 998244353;
int n, a[N], k, pre[N], suf[N];
ll sp[N], ss[N];

constexpr ll qpow(ll a, ll b)
{
    if(!b) return 1;
    return ((b & 1) ? a : 1ll) * qpow(a * a % p, b >> 1) % p;
}

constexpr ll inv4 = qpow(4, p - 2);

struct BIT {
    int a[N];
    void upd(int q, int v) {for(int i = q; i < N; i += (i & -i)) a[i] += v;}
    int qry(int q) {int ans = 0; for(int i = q; i; i -= (i & -i)) ans += a[i]; return ans; }
    int qry(int ql, int qr) {return qry(qr) - qry(ql - 1);}
} t, t2;

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> k;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++)
    {
        pre[i] = t.qry(a[i] + 1, n);
        t.upd(a[i], 1);
        sp[i] = (sp[i - 1] + pre[i]) % p;
    }
    memset(t.a, 0, sizeof t.a);
    for(int i = n; i >= 1; i --)
    {
        suf[i] = t.qry(1, a[i] - 1);
        t.upd(a[i], 1);
        ss[i] = (ss[i + 1] + suf[i]) % p;
    }
    memset(t.a, 0, sizeof t.a);
    for(int i = k; i <= n; i ++)
        t.upd(a[i], 1);
    ll ans = 0, Erev = 1ll * k * (k - 1) % p * inv4 % p;
    ll now = 0;
    for(int i = 1; i <= n - k + 1; i ++)
    {
        ll res = 0;
        res += ss[1] - ss[i];
        res += sp[n] - sp[i + k - 1];
        
        t.upd(a[i + k - 1], -1);

        now -= t2.qry(a[i + k - 1] + 1, n);
        if(a[i - 1] > 1) now += t.qry(1, a[i - 1] - 1);
        
        if(i > 1) t2.upd(a[i - 1], 1);
        
        res -= now;
        res += Erev;
        ans = (ans + res) % p;
    }
    cout << (ans * qpow(n - k + 1, p - 2) % p + p) % p;

    return 0;
}