[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;
}
上一篇:[NOI Online #1 提高组]最小环


下一篇:[NOI Online #3 提高组]优秀子序列