JZOJ 5222. 【GDOI2018模拟7.12】A (Standard IO)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/er111er/article/details/84675358

题目链接:
Click here

Solution

v a l i val_i 表示 i i 与它后面的数形成的逆序对个数。 a n s ans 为原来数列逆序对个数。
每次询问一个 x x ,对于满足 i > = x i>=x 并且 a i < = a x a_i<=a_x 的所有 i i ,若 j > i j>i 并且 a j < a i a_j<a_i ,那么 ( i , j ) (i,j) 形成了逆序对。但由于 a j < a i < = a x a_j<a_i<=a_x ,所以这些 j j 都会被排好序,变成了 j > i j>i 并且 a j > a i a_j>a_i ,这些 i i 的逆序对也就全都被消去了,也就是说 a n s ans 要减去这些 v a l i val_i
直接上树套树orCDQ?不存在的。因为一个 i i 在某一次询问被消去影响之后,就不会再被消去,所以在线的做法没法做。那么考虑离线,按照询问的修改位置排序,询问 x x 能够消去位置 i i 的影响的条件是 x < = i x<=i a i < = a x a_i<=a_x 。设 t x t_x 表示 x x 是第几个询问,那么 i i 最早消去影响的时间就是满足 x < = i x<=i a i < = a x a_i<=a_x 的最小的 t x t_x
这是个二维偏序问题,按照 x x 排序后一维被消掉,条件 a i < = a x a_i<=a_x 用树状数组维护后缀最小值即可。
至于当询问 y y 对应的 a y a_y 被某个询问 x x 修改的情况,我们只需要用原来没被修改过的的 a y a_y 即可,因为如果 y y x x 改了,说明 y y 后面的逆序对早就被询问 x x 消完了,我们即使把这个询问加进去也不会影响答案。
然后此题完。

Code

由于using namespace std被ban掉了,为了巩固基础知识,我自己实现了归并排序、离散化、二分等算法,导致码量大了一点,但是让我对基础算法理解加深了更多。

#include <cstdio>
#include <cstring>
#include <cstdlib>

typedef long long ll;
const int N = 100007;
const ll INF = 0x3f3f3f3f;
int min(int a, int b) { return a < b ? a : b; }

int n, m, len, a[N], val[N], arr[N], temp[N];
ll sum[N];

struct note { int po, tim, val; } q[N], tmp[N];
int operator<(note a, note b) { return a.po < b.po; }

void sortq(int l, int r)
{
	if (l == r) return;
	int mid = l + r >> 1;
	sortq(l, mid), sortq(mid + 1, r);
	int i = l, j = mid + 1, len = 0;
	while (j <= r)
	{
		while (i <= mid && q[i] < q[j]) tmp[++len] = q[i++];
		tmp[++len] = q[j++];
	}
	while (i <= mid) tmp[++len] = q[i++];
	for (int i = 1; i <= len; i++) q[l + i - 1] = tmp[i];
}
void sort(int l, int r)
{
	if (l == r) return;
	int mid = l + r >> 1;
	sort(l, mid), sort(mid + 1, r);
	int i = l, j = mid + 1, len = 0;
	while (j <= r)
	{
		while (i <= mid && arr[i] <= arr[j]) temp[++len] = arr[i++];
		temp[++len] = arr[j++];
	}
	while (i <= mid) temp[++len] = arr[i++];
	for (int i = 1; i <= len; i++) arr[l + i - 1] = temp[i];
}
void unique()
{
	len = 0;
	int i = 1;
	while (i <= n)
	{
		arr[++len] = arr[i++];
		while (i <= n && arr[i] == arr[i - 1]) i++;
	}
}
int lower_bound(int val)
{
	int l = 1, r = len, mid, ans;
	while (l <= r)
	{
		mid = l + r >> 1;
		if (arr[mid] >= val) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	return ans;
}
int tr[N][2];
void add(int po, int k, int v)
{
	for (; po <= n; po += (po & (-po)))
	{
		if (k) tr[po][k] = min(tr[po][k], v);
		else tr[po][k] += v;
	}
}
int query(int po, int k)
{
	int ret = k ? INF : 0;
	for (; po; po -= (po & (-po)))
	{
		if (k) ret = min(ret, tr[po][k]);
		else ret += tr[po][k];
	}
	return ret;
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%d", a + i);
	memcpy(arr, a, sizeof(a));
	sort(1, n);
	unique();
	for (int i = n; i >= 1; i--) a[i] = lower_bound(a[i]), val[i] = query(a[i] - 1, 0), add(a[i], 0, 1), sum[0] += val[i];
	for (int i = 1; i <= m; i++) scanf("%d", &q[i].po), q[i].tim = i, q[i].val = a[q[i].po];
	sortq(1, m);
	for (int i = 1; i <= n; i++) tr[i][1] = INF;
	for (int i = 1, j = 1; i <= n; i++)
	{
		while (j <= m && q[j].po <= i) add(len - q[j].val + 1, 1, q[j].tim), j++;
		int ret = query(len - a[i] + 1, 1);
		if (ret < INF) sum[ret] -= val[i];
	}
	printf("%lld\n", sum[0]);
	for (int i = 1; i <= m; i++) sum[i] += sum[i - 1], printf("%lld\n", sum[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/er111er/article/details/84675358
今日推荐