IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> P5046 Yuno loves sqrt technology I -> 正文阅读

[数据结构与算法]P5046 Yuno loves sqrt technology I

P5046 Yuno loves sqrt technology I

给你一个长为 n n n 的排列, m m m 次询问,每次查询一个区间的逆序对数,强制在线。

1 ≤ n , m ≤ 1 0 5 1 \leq n,m\leq 10^5 1n,m105,时限 750 ms 750\text{ms} 750ms,空限 500 MB 500\text{MB} 500MB

sol

静态查询逆序对数。

根据这题没有修改,容易想到直接预处理, O ( n ) \mathcal O(\sqrt{n}) O(n ?) 内回答询问即可。

考虑序列分块。

预处理方法一

类似于归并排序,需要预处理的信息有:

  1. 每个元素到其块首这段区间的逆序对数。
  2. 每个元素到其块尾这段区间的逆序对数。
  3. i i i 到块 j j j 这段区间的逆序对数。
  4. i i i 个块中,小于等于 j j j 的元素个数。

对于 1 , 2 1,2 1,2,用树状数组扫一遍即可,单块时间复杂度为 O ( n log ? n ) \mathcal O(\sqrt n \log \sqrt n) O(n ?logn ?),总时间复杂度为 O ( n log ? n ) \mathcal O(n \log \sqrt n) O(nlogn ?),可过。

对于 3 3 3,设其为 f [ i ] [ j ] f[i][j] f[i][j],则有递推公式:
f [ i ] [ j ] = f [ i ] [ j ? 1 ] + f [ i + 1 ] [ j ] ? f [ i + 1 ] [ j ? 1 ] + ( ? i ?所在块和? j ?所在块产生的贡献 ) f[i][j]=f[i][j-1]+f[i+1][j]-f[i+1][j-1]+(\ i \ \text{所在块和}\ j \ \text{所在块产生的贡献}) f[i][j]=f[i][j?1]+f[i+1][j]?f[i+1][j?1]+(?i?所在块和?j?所在块产生的贡献)
时间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn ?),可过。

对于 4 4 4,对于每一个 j j j,先在每一个块内扫一遍,再计算前缀和即可,时间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn ?),可过。

预处理好了,接下来考虑怎么查询,对于一个询问 l , r l,r l,r

  • l , r l,r l,r 在同一块内,则答案为块首到 r r r 的贡献和块首到 l ? 1 l-1 l?1 的贡献之差,内部贡献用 1 1 1 计算,两块贡献用归并求即可。

  • l , r l,r l,r 不在同一块内,则分成两个散块和多个整块,先用 3 3 3 计算好整块的内部贡献,然后根据 4 4 4 求散块与整块的贡献,再归并求散块对散块的贡献,最后散块内部贡献用上面那种方法计算即可。

总时间复杂度为 O ( ( n + m ) n ) \mathcal O((n+m)\sqrt{n}) O((n+m)n ?),总空间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn ?)

预处理方法二

F [ i ] [ j ] F[i][j] F[i][j] 表示 [ l , r ] [l,r] [l,r] 这段区间与第 j j j 块中的数产生的逆序对数,记为 5 5 5

再把法一中的 1 , 2 , 3 1,2,3 1,2,3 照搬过来。

考虑计算 F F F,对于每个块,对于每个块,归并记录每个数对块的贡献,然后算一遍前缀和即可,时间复杂度 O ( n n ) \mathcal O(n \sqrt n) O(nn ?),可过。

这样计算 f f f 的时候可以省掉一个 O ( n ) \mathcal O(\sqrt n) O(n ?) 的归并。

对于询问 l , r l,r l,r

  • l , r l,r l,r 在同一块内,则答案为块首到 r r r 的贡献和块首到 l ? 1 l-1 l?1 的贡献之差,内部贡献用 1 1 1 计算,两块贡献用归并求即可。
  • l , r l,r l,r 不在同一块内,则分成两个散块和多个整块,先用 3 3 3 计算好整块的内部贡献,然后根据 5 5 5 差分求散块与整块的贡献,再归并求散块对散块的贡献,最后散块内部贡献用上面那种方法计算即可。

总时间复杂度为 O ( ( n + m ) n ) \mathcal O((n+m)\sqrt{n}) O((n+m)n ?),总空间复杂度为 O ( n n ) \mathcal O(n \sqrt n) O(nn ?),但常数较法一更为优秀。

01-31?/?21:29:13?/?2.91s?/?132.38MB?/?4.00KB?C++98?O2 \text{01-31 / 21:29:13 / 2.91s / 132.38MB / 4.00KB C++98 O2} 01-31?/?21:29:13?/?2.91s?/?132.38MB?/?4.00KB?C++98?O2

#include <cstdio>
#include <algorithm>

namespace Fread
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S, *T;
    inline char getchar()
    {
        if (S == T)
        {
            T = (S = buf) + fread(buf, 1, SIZE, stdin);
            if (S == T)
                return '\n';
        }
        return *S++;
    }
}
namespace Fwrite
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S = buf, *T = buf + SIZE;
    inline void flush()
    {
        fwrite(buf, 1, S - buf, stdout);
        S = buf;
    }
    inline void putchar(char c)
    {
        *S++ = c;
        if (S == T)
            flush();
    }
    struct NTR
    {
        ~NTR()
        {
            flush();
        }
    } ztr;
}

#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

inline void write(long long x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

const int _ = 1e5 + 7, M = 320, siz = 317;

int n, m, a[_], L[M], R[M], bel[_], blo, pre[_], suf[_], f[M][_];

long long F[M][M], ans;

int x[M], y[M], lx, ly, c[_], d[_];

struct Pair
{
	int fi, se;
	inline bool operator < (const Pair & tmp) const
	{
		return fi < tmp.fi;
	}
} b[_];

struct BIT
{
	int b[_];
	inline void add(int x, int val)
	{
		for(int i = x; i < _; i += i & -i) b[i] += val;
	}
	inline int ask(int x)
	{
		int res = 0;
		for(int i = x; i; i -= i & -i) res += b[i];
		return res;
	}
} t;

inline int merge(int *a, int *b, int la, int lb)
{
	int ia = 1, ib = 1, res = 0;
	while(ia <= la && ib <= lb)
		if(a[ia] < b[ib]) ++ia;
		else
		{
			res += la - ia + 1;
			++ib;
		}
	return res;
}

inline void init()
{
	blo = (n - 1) / siz + 1;
	for(int i = 1; i <= blo; ++i)
	{
		L[i] = R[i - 1] + 1;
		R[i] = i * siz;
	}
	R[blo] = n;
	for(int i = 1; i <= n; ++i)
		b[i] = (Pair)
	{
		a[i], i
	};
	for(int i = 1; i <= blo; ++i)
	{
		std::sort(b + L[i], b + R[i] + 1);
		for(int j = L[i]; j <= R[i]; ++j)
		{
			bel[j] = i;
			c[j] = b[j].fi;
			d[j] = b[j].se;
		}
		int x = 0;
		for(int j = L[i]; j <= R[i]; ++j)
		{
			t.add(a[j], 1);
			x += t.ask(n) - t.ask(a[j]);
			pre[j] = x;
		}
		F[i][i] = x;
		for(int j = L[i]; j <= R[i]; ++j)
		{
			suf[j] = x;
			t.add(a[j], -1);
			x -= t.ask(a[j] - 1);
		}
	}
	std::sort(b + 1, b + n + 1);
	for(int j = 1; j <= blo; ++j)
		for(int i = 1, k = L[j]; i <= n; ++i)
		{
			const int id = b[i].se;
			while(k <= R[j] && b[i].fi > c[k]) ++k;
			if(id < L[j]) f[j][id] = k - L[j];
			else if(id > R[j]) f[j][id] = R[j] - k - (k <= R[j] && b[i].fi == c[k]) + 1;
		}
	for(int i = 1; i <= blo; ++i)
		for(int j = 2; j <= n; ++j) f[i][j] += f[i][j - 1];
	for(int len = 1; len <= blo; ++len)
		for(int i = 1; i <= blo; ++i)
		{
			if(len + i > blo) break;
			const int j = i + len;
			F[i][j] = F[i + 1][j] + F[i][j - 1] - F[i + 1][j - 1] + f[j][R[i]] - f[j][L[i] - 1];
		}
}

signed main()
{
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	init();
	while(m--)
	{
		int l = read() ^ ans, r = read() ^ ans, bl = bel[l], br = bel[r];
		lx = ly = 0;
		if(bl == br)
		{
			for(int i = L[bl]; i <= R[bl]; ++i)
			{
				if(l <= d[i] && d[i] <= r) y[++ly] = c[i];
				else if(d[i] < l) x[++lx] = c[i];
				ans = pre[r] - ((l == L[bl]) ? 0 : pre[l - 1]) - merge(x, y, lx, ly);
			}
		}
		else
		{
			ans = F[bl + 1][br - 1] + pre[r] + suf[l];
			for(int i = bl + 1; i <= br - 1; ++i)
				ans += f[i][R[bl]] - f[i][l - 1] + f[i][r] - f[i][L[br] - 1];
			for(int i = L[bl]; i <= R[bl]; ++i)
				if(d[i] >= l) x[++lx] = c[i];
			for(int i = L[br]; i <= R[br]; ++i)
				if(d[i] <= r) y[++ly] = c[i];
			ans += merge(x, y, lx, ly);
		}
		write(ans), putchar('\n');
	}
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-02-01 20:51:59  更:2022-02-01 20:53:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 17:25:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码