题意:给定长为 n n n 的序列,对于每个 k ∈ [ 1 , n ] k \in [1, n] k∈[1,n] 问在每个长为 k k k 的子段中都出现过的数中最小的是多少(可能不存在)。
对序列中每种数 a a a 看其之间最大间隔是多少,设为 t t t,那么所有 ≥ t \ge t ≥t 的 k k k 的答案存在,且至多是 a a a。
于是做一个后缀 min 即可。
题意:给定长为 n n n 的正整数序列,每次可以选定 i , j ∈ [ 1 , n ] i, j \in [1, n] i,j∈[1,n] 和 x ≥ 0 x \ge 0 x≥0,让 a i a_i ai 减少 i x ix ix,让 a j a_j aj 增加 i x ix ix。需保证操作完 a i ≥ 0 a_i \ge 0 ai≥0。问能否在 3 n 3n 3n 次操作内让所有数均相同,能的话给出方案。
这是那种一眼看上去就大概能会的题,但是我 WA 了 13 发…
无解容易判定:只要和不是 n n n 的倍数就无解。后面构造的基本思想是先对于所有 > 1 >1 >1 的 i i i,收集 a i a_i ai 到 a 1 a_1 a1,然后再让 a 1 a_1 a1 把所有其他数变成目标(就是最后要全部变成的那个数)。
所谓收集,指的就是先用一次操作,从 a 1 a_1 a1 借走一定大小,让 a i a_i ai 变成能被 i i i 整除,然后用一次操作把 a i a_i ai 直接加到 a 1 a_1 a1 上。
这里就比较 trick:我一开始以为 a i < i a_i < i ai<i 时是直接无解,于是搞了很久,后面又尝试了各种方法才勉强通过。后面看题解发现收集这一阶段其实只要按照 i = 2 , … , n i=2, \ldots, n i=2,…,n 收集即可。
原因很简单,由于原始序列是正整数序列,从而初始时 a 1 ≥ 1 a_1 \ge 1 a1≥1。我们可以归纳证明这么收集到 a i a_i ai 时, a 1 ≥ i a_1 \ge i a1≥i。那么这就保证了上面的收集操作不会出现 a 1 a_1 a1 不够借给 a i a_i ai 的情况。
题意:给定一个序列,找出最小的 x x x 使得原序列每个数异或上 x x x 后逆序对数最小。
从高到低做,每次看要不要在最高位上异或一个 1。无论要不要,我们最后都不需要考虑最高位不同的数之间逆序对的贡献(因为这一步已经算完了)。
然后类似归并,把原序列分割为两个子序列(子序列中的数保持原有顺序),一个子序列中所有数最高位为 0,另一个为 1。现在逆序对只会来自这两个子序列各自的内部。于是递归下去计算即可。
int n, a[300005], maxi, b[300005];
set<int> endpoint;
int llst[300005], ttot;
pair<ll, ll> get(int l, int r, int highbit){
// return number of inversions
ll inv1 = 0, inv2 = 0;
int cnt0 = 0, cnt1 = 0;
REP(i, l, r){
if (a[i] & highbit){
inv2 += cnt0;
++cnt1;
} else {
inv1 += cnt1;
++cnt0;
}
}
return make_pair(inv1, inv2);
}
int split(int l, int r, int highbit){
int ccnt = 0;
REP(i, l, r){
if (!(a[i] & highbit)) ++ccnt;
}
if (ccnt == r - l + 1) return -1;
int lpos = l, rpos = l + ccnt;
REP(i, l, r){
if (!(a[i] & highbit)) b[lpos++] = a[i];
else b[rpos++] = a[i];
}
REP(i, l, r) a[i] = b[i];
return l + ccnt - 1;
}
void init(){
n = read();
maxi = 0;
REP(i, 1, n) a[i] = read(), maxi = max(maxi, a[i]);
}
void solve(){
int logg = 1;
while (logg <= maxi) logg <<= 1;
logg >>= 1;
endpoint.insert(n);
int x = 0;
ll totinv = 0;
while (logg > 0){
int lst = 1;
ll inv0 = 0, inv1 = 0;
for (int x: endpoint){
auto p = get(lst, x, logg);
inv0 += p.first, inv1 += p.second;
lst = x + 1;
}
if (inv0 > inv1){
x |= logg;
REP(i, 1, n) a[i] ^= logg;
}
totinv += min(inv0, inv1);
lst = 1;
ttot = 0;
for (int x: endpoint){
int mid = split(lst, x, logg);
if (mid > 0) llst[++ttot] = mid;
lst = x + 1;
}
REP(i, 1, ttot) endpoint.insert(llst[i]);
logg >>= 1;
}
printf("%lld %d\n", totinv, x);
}
待补。。。
待补。。。
待补。。。