【分治】CF1416C
这题是我练 CF DP 标签题目的时候遇到的,但我做完之后根本没见到 DP 的影子啊。。
https://codeforces.com/contest/1416/problem/C
分析
考虑从高位到低位拆位决策。
从最高位考虑起,记为第 \(k\) 位。
采取分治的思想,只考虑第 \(k\) 位造成的贡献,也就是说,我们可以将第 \(k\) 位为 \(0, 1\) 的数分成 \(l, r\) 两份,可以发现二者的逆序对数贡献互不干扰,递归下去即可。
现在的问题转化为给你一个 \(0, 1\) 序列,然后统计逆序对数,结果记为 \(fir\)。
这个问题很简单,我们就统计每次枚举到 \(0\) 的时候前缀有多少个 \(1\) 即可。
然后我们将原 \(0, 1\) 序列取反,再统计一次逆序对数,作为第 \(k\) 位如果选择取反后的逆序对数,记为 \(sec\)。
最后做个贪心,假如第 \(k\) 层所有 \(fir\) 的和小于等于 \(sec\) 的和,那就没必要取反,否则为了最小化逆序对数必须取反。
实现
// Problem: C. XOR Inverse
// Contest: Codeforces - Codeforces Round #673 (Div. 1)
// URL: https://codeforces.com/problemset/problem/1416/C
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define x first
#define y second
using ll = long long;
using pii = pair<ll, ll>;
inline void read(int &x){
int s=0; x=1;
char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
const int N=3e5+5;
int n;
vector<int> w;
ll res, det;
vector<pii> c[35];
void solve(vector<int> &w, int k){
if(k==-1 || !w.size()) return;
vector<int> l, r;
vector<int> b;
int n=w.size();
rep(i, 0, n-1){
int val=w[i]>>k&1;
b.pb(val);
if(!val) l.pb(w[i]);
else r.pb(w[i]);
}
int cnt=0;
ll fir=0, sec=0;
for(auto &i: b){
if(i) cnt++;
else fir+=cnt;
i^=1;
}
cnt=0;
for(auto &i: b){
if(i) cnt++;
else sec+=cnt;
}
c[k].pb({fir, sec});
solve(l, k-1), solve(r, k-1);
}
signed main(){
cin>>n;
w.resize(n);
rep(i, 0, n-1) read(w[i]);
solve(w, 30);
rep(i, 0, 30){
ll fir=0, sec=0;
for(auto [x, y]: c[i]) fir+=x, sec+=y;
if(fir>sec) det|=(1<<i);
res+=min(fir, sec);
}
cout<<res<<" "<<det<<endl;
return 0;
}