1. 程式人生 > >JZOJ 5222. 【GDOI2018模擬7.12】A (Standard IO)

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

題目連結:
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 (