1. 程式人生 > 實用技巧 >題解 P5046 【[Ynoi2019模擬賽]Yuno loves sqrt technology I】

題解 P5046 【[Ynoi2019模擬賽]Yuno loves sqrt technology I】

題意簡述

無修改區間求逆序對。

題解

首先有一個顯然的 \(\Theta(N\sqrt{N}\log_{2}N)\) 做法,由於過不了所以我就不廢話。

其實有了 \(\Theta(N\sqrt{N}\log_{2}N)\) 的過不去做法,我們就可以根據這個思路然後預處理解決問題。

我們需要處理的資訊有:

  1. 散塊的逆序對數量

  2. 以塊為單位的區間逆序對數量

那麼我們需要處理的陣列就有以下幾個:

  1. previous[i] 表示 \(i\) 到 該塊開頭的逆序對數量。

  2. suffix[i] 同理。

  3. block[i][j] 表示前 \(i\) 個塊中 \(\leq j\) 元素個數。

  4. intervals[i][j]

    表示以塊為單位的區間 \([i,j]\) 中的逆序對數量。

講講預處理方法。

  1. previous[i]suffix[i] 的處理方法都很顯然,可以一直掃著然後FWT掃就行。

  2. block[i][j] 可以遞推,遞推式為 block[i][j]=block[i+1][j]+block[i][j-1]-block[i+1][j-1]+cont(i,j)。其中 cont(i,j) 表示計算對應塊的逆序對數。

  3. intervals[i][j] 每次迴圈到塊的開頭繼承上一個塊的貢獻即可。

計算貢獻的方法很簡單,歸併即可。mrsrz講得也挺清楚的,我這裡就不再贅述,主要講講怎麼卡常。

首先我們可以把主函式裡的所有迴圈全部展開,經過實踐引數傳8的時候跑得比較快。

然後八聚氧先加上,luogu O2也開著。

再其次快讀fread快輸fwrite,這些都是卡常的標配。

然後就把能拿出來的結構體拿出來,實在不能就不管了。

然後去STL,pair vector能去就去。

然後long long開在正確的地方,不要無腦replace。

函式inline,迴圈register。雖然可能作用不大但是可以先加上。

然後調塊長,經過無數次實踐發現取150~170較為優秀。

然後加了過後發現就算rp再好也只有60pts。

然後谷歌搜尋硫酸的化學式H₂SO₄,給評測機喂硫酸(idea來自SyadouHayami)。

然後本來交了5頁都過不了,這下再交兩次就過了。

// 省略八聚氧
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int Maxn = 1e5 + 5;
const int Maxm = 650;
const int each = 160;
int n, m, blocks, Lp[Maxn], Rp[Maxn], isa[Maxn], head[Maxn], tail[Maxn], sorted[Maxn], belong[Maxn], previous[Maxn], suffix[Maxn], block[Maxm][Maxn];
long long intervals[Maxm][Maxn];
struct Holy_Pair
{
	int first, second;

	bool operator < (const Holy_Pair& rhs) const
	{
		return first < rhs.first;
	}
} current[Maxn];
struct Fenwick_Tree
{
	int fwt[Maxn];
	
	inline void Modify(int x, int v)
	{
		for (; x + 5 <= Maxn; x += x & -x)	fwt[x] += v;
	}

	inline int Query(int x)
	{
		int ret = 0;
		for (; x; x ^= x & -x)	ret += fwt[x];
		return ret;
	}
} FWT;

#define io_e '\0'
#define io_s ' '
#define io_l '\n'
namespace Fast_IO
{
... // 省略快讀
}  // namespace Fast_IO

using Fast_IO::read;
using Fast_IO::write;

inline Holy_Pair make_pair(int first, int second)
{
	Holy_Pair ret;
	ret.first = first;
	ret.second = second;
	return ret;
}

inline int Merge_Vct(int rhs[], int shr[], int szl, int szr)
{
	int itl = 1, itr = 1;
	int ret = 0, ctr = 1;
	while (itl <= szl && itr <= szr)
	{
		if (rhs[itl] < shr[itr]) 	++itl, ++ctr;
		else
		{
			ret += szl - ctr + 1;
			++itr;
		}
	}
	return ret + szr - szr;
}

inline int Merge_Idx(int st1, int st2, int sz1, int sz2)
{
	int ret = 0, id1 = st1 + 1, id2 = st2 + 1;
	sz1 += st1, sz2 += st2;
	while (id1 <= sz1 && id2 <= sz2)
	{
		if (sorted[id1] < sorted[id2])		++id1;
		else
		{
			ret += sz1 - id1 + 1;
			++id2;
		}
	}
	return ret;
}

inline void Behavior(int l, int r, long long &ans)
{
	int itl = 0, itr = 0;
	if (belong[l] == belong[r])
	{
		for (int i = head[belong[l]]; i <= tail[belong[r]]; ++i)
		{
			if (current[i].second >= l && current[i].second <= r)	Rp[++itr] = sorted[i];
			else if (current[i].second < l)		Lp[++itl] = sorted[i];
		}
		if (l == head[belong[l]])	ans = previous[r] - Merge_Vct(Lp, Rp, itl, itr);
		else 	ans = previous[r] - previous[l - 1] - Merge_Vct(Lp, Rp, itl, itr);
	}
	else
	{
		ans = intervals[belong[l] + 1][belong[r] - 1] + previous[r] + suffix[l];
		for (int i = head[belong[l]]; i <= tail[belong[l]]; ++i)
		{
			if (current[i].second >= l)
			{
				Lp[++itl] = sorted[i];
				ans += block[belong[r] - 1][1] - block[belong[r] - 1][sorted[i]] - block[belong[l]][1] + block[belong[l]][sorted[i]];
			}
		}
		for (int i = head[belong[r]]; i <= tail[belong[r]]; ++i)
		{
			if (current[i].second <= r)
			{
				Rp[++itr] = sorted[i];
				ans += block[belong[r] - 1][sorted[i] + 1] - block[belong[l]][sorted[i] + 1];
			}
		}
		ans += Merge_Vct(Lp, Rp, itl, itr);
	}
	write(io_l, ans);
}

signed main()
{
	read(n, m), blocks = (n - 1) / each + 1;
	if (n <= 8)
	{
		for (int i = 1; i <= n; ++i)
		{
			read(isa[i]);
			current[i] = make_pair(isa[i], i);
		}
	}
	else
	{
		#pragma unroll 8
		for (int i = 1; i <= n; ++i)
		{
			read(isa[i]);
			current[i] = make_pair(isa[i], i);
		}
	}
	if (blocks <= 8)
	{
		for (int i = 1; i <= blocks; ++i)
		{
			head[i] = tail[i - 1] + 1;
			tail[i] = tail[i - 1] + each;
			if (i == blocks) 	tail[i] = n;
		}
	}
	else
	{
		#pragma unroll 8
		for (int i = 1; i <= blocks; ++i)
		{
			head[i] = tail[i - 1] + 1;
			tail[i] = tail[i - 1] + each;
			if (i == blocks) 	tail[i] = n;
		}
	}
	if (blocks <= 8)
	{
		for (int i = 1; i <= blocks; ++i)
		{
			memcpy(block[i], block[i - 1], sizeof(block[0]));
			sort(current + head[i], current + 1 + tail[i]);
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				++block[i][isa[j]];
				belong[j] = i;
				sorted[j] = current[j].first;
			}
			int satisfy = 0;
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				FWT.Modify(isa[j], 1);
				satisfy += FWT.Query(n) - FWT.Query(isa[j]);
				previous[j] = satisfy;
			}
			intervals[i][i] = satisfy;
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				suffix[j] = satisfy;
				FWT.Modify(isa[j], -1);
				satisfy -= FWT.Query(isa[j] - 1);
			}
		}
	}
	else
	{
		#pragma unroll 8
		for (int i = 1; i <= blocks; ++i)
		{
			memcpy(block[i], block[i - 1], sizeof(block[0]));
			sort(current + head[i], current + 1 + tail[i]);
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				++block[i][isa[j]];
				belong[j] = i;
				sorted[j] = current[j].first;
			}
			int satisfy = 0;
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				FWT.Modify(isa[j], 1);
				satisfy += FWT.Query(n) - FWT.Query(isa[j]);
				previous[j] = satisfy;
			}
			intervals[i][i] = satisfy;
			for (int j = head[i]; j <= tail[i]; ++j)
			{
				suffix[j] = satisfy;
				FWT.Modify(isa[j], -1);
				satisfy -= FWT.Query(isa[j] - 1);
			}
		}
	}
	if (blocks <= 8)
	{
		for (int dis = 1; dis <= blocks; ++dis)
		{
			for (int i = n - 1; i; --i)		block[dis][i] += block[dis][i + 1];
			for (int l = 1, r = dis + 1; r <= blocks + 1; ++l, ++r)
				intervals[l][r] = intervals[l + 1][r] + intervals[l][r - 1] - intervals[l + 1][r - 1] +
									Merge_Idx(head[l] - 1, head[r] - 1, tail[l] - head[l] + 1, tail[r] - head[r] + 1);
		}
	}
	else
	{
		#pragma unroll 8
		for (int dis = 1; dis <= blocks; ++dis)
		{
			for (int i = n - 1; i; --i)		block[dis][i] += block[dis][i + 1];
			for (int l = 1, r = dis + 1; r <= blocks + 1; ++l, ++r)
				intervals[l][r] = intervals[l + 1][r] + intervals[l][r - 1] - intervals[l + 1][r - 1] +
									Merge_Idx(head[l] - 1, head[r] - 1, tail[l] - head[l] + 1, tail[r] - head[r] + 1);
		}
	}

	if (m <= 8)
	{
		long long lastans = 0;
		for (int i = 0; i < m; ++i)
		{
			long long l, r;
			read(l, r);
			l ^= lastans;
			r ^= lastans;
			Behavior(l, r, lastans);
		}
	}
	else
	{
		long long lastans = 0;
		#pragma unroll 8
		for (int i = 0; i < m; ++i)
		{
			long long l, r;
			read(l, r);
			l ^= lastans;
			r ^= lastans;
			Behavior(l, r, lastans);
		}
	}
	return 0;
}