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;
}