[NOI Online #1 提高组]冒泡排序
传送门
不得不说,NOI Online的质量确实不错,有一定的思维难度,而且比较新颖。
怎么说呢,这题我差点就搞出来了。
这题刚开始我一点思路都没有,后来经过大量的手模后发现了一些规律,知道这个规律后,题目的一大半就解决了。
记在前\(i\)个数中,比\(a_i\)大的数的个数为\(b_i\),那么每一轮冒泡排序后,所有\(b_i\)都会-1,直到\(b_i\)等于0.
说白了,就是让求
\[\sum_{i=1}^{n} max \{b_i - x, 0 \}
\]
这个结论的证明,可以这么想:如果在\(a_i\)前面有比\(a_i\)大的,那么这其中最大的数一定会在冒泡排序中和\(a_i\)交换,那么\(b_i\)就少了1,而一轮冒泡排序中能移动一个比\(a_i\)大的数,所以只会是减1,而不是变成其他的值。
那这东西怎么求呢?我就卡在了这里,气死了。
其实我们只要维护一个权值线段树(树状数组),每一次求大于\(x\)的\(b_i\)个数及其值的和就行了……
结果我维护了一个序列线段树,然后用最大最小值一顿乱搞剪枝,拿了60分……唉。
询问到这就完事了,至于修改,也很简单。
因为只是相邻的两个数交换,所以只影响\(b_i\)和\(b_{i+1}\)。
如果\(a_i < a_{i+1}\),那么\(b'_i = b_{i+1}, b'_{i+1} = b_i+1\)。
如果\(a_i > a_{i+1}\),那么\(b'_i = b_{i+1}-1, b'_{i+1}=b_i\)。
在树状数组上单点修改即可。
上代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
In ll read()
{
ll ans = 0;
char ch = getchar(), las = ' ';
while(!isdigit(ch)) las = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(las == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen("ha2.in", "r", stdin);
freopen("ha.out", "w", stdout);
#endif
}
int n, m, a[maxn], b[maxn];
int c[maxn];
In int lowbit(int x) {return x & -x;}
In void A(int x)
{
for(; x <= n; x += lowbit(x)) c[x]++;
}
In int Q(int x)
{
int ret = 0;
for(; x; x -= lowbit(x)) ret += c[x];
return ret;
}
ll c2[maxn], cnum[maxn];
In void update(int x, int d)
{
ll tp = x;
for(int i = n - x + 1; i <= n + 1; i += lowbit(i)) c2[i] += tp * d, cnum[i] += d;
}
In ll query(int x)
{
if(x >= n) return 0;
ll sum = 0, num = 0;
for(int i = n - x + 1; i; i -= lowbit(i)) sum += c2[i], num += cnum[i];
return sum - num * x;
}
int main()
{
// MYFILE();
n = read(), m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i)
{
b[i] = Q(n - a[i] + 1);
A(n - a[i] + 1);
}
for(int i = 1; i <= n; ++i) update(b[i], 1);
for(int i = 1; i <= m; ++i)
{
int op = read(), x = read();
if(op == 1)
{
int tp1 = b[x], tp2 = b[x + 1];
if(a[x] < a[x + 1])
{
b[x] = tp2, b[x + 1] = tp1 + 1;
update(tp1, -1), update(tp1 + 1, 1);
}
else
{
b[x] = tp2 - 1, b[x + 1] = tp1;
update(tp2, -1), update(tp2 - 1, 1);
}
swap(a[x], a[x + 1]);
}
else write(query(x)), enter;
}
return 0;
}