首页 > 其他分享 >【Ynoi 2019 模拟赛】Yuno loves sqrt technology II

【Ynoi 2019 模拟赛】Yuno loves sqrt technology II

时间:2024-09-22 19:23:22浏览次数:1  
标签:lower idx int Ynoi sqrt II cnt 莫队

Luogu P5047 Yuno loves sqrt technology II

题意

给定一个长度为 \(n\) 的序列 \(a\)。

有 \(m\) 次询问:查询区间 \([l,r]\) 中的逆序对数。

数据范围与约定

\(1 \le n,m \le 10^5\),\(0 \le a_i \le 10^9\)。

题解

看到区间问题,先思考线段树。发现用线段树没法合并两个区间的信息。所以考虑分块。分块确实能做,但是这题卡空间,把分块给 ban 了。于是考虑能不能把询问差分成 \([1,r]-[1,l-1]\) 的形式。发现也拆不开。 那就考虑莫队。

莫队直接做很容易,可以用 \(O(n\sqrt{m}logn)\) 的时间复杂度解决。太慢了,而且莫队不像分块,不能把这个 \(logn\) 放到根号内。

while (query[i].l < l) 为例。假设移动前区间为 \([l,r]\),移动后区间为 \([l',r]\),其中 \(l'=l-1\)。

左指针向左移动,区间被扩大了,多增加了一个元素 \(a_{l'}\)。其对当前询问产生的贡献为 \([l,r]\) 中小于 \(a_{l'}\) 的数的个数。

普通的莫队,这里会维护一个全局的平衡树或者树状数组或者别的数据结构,用 \(O(logn)\) 的时间复杂度内求出这个贡献。这是没办法继续优化的,所以考虑这个地方还有没有别的手段。

为了方便,我们令 \(cnt_{[l,r],lower,x}\) 表示区间 \([l,r]\) 中小于 \(x\) 的数的个数。

差分后发现,\(cnt_{[l,r],lower,a_{l'}}=cnt_{[l,n],lower,a_{l'}}-cnt_{[r+1,n],lower,a_{l'}}\)。

并且其中 \(cnt_{[l,n],lower,a_{l'}}=cnt_{[l',n],lower,a_{l'}}\)。因为要求的是小于,所以自己并不会对自己做出贡献。

所以代入进去就变成:\(cnt_{[l,r],lower,a_{l'}}=cnt_{[l',n],lower,a_{l'}}-cnt_{[r+1,n],lower,a_{l'}}\)。

观察这个式子,我们发现,\(cnt_{[l',n],lower,a_{l'}}\) 非常的好求,从后往前扫一遍预处理一下就行了,复杂度 \(O(nlogn)\),每次查询都可以 \(O(1)\) 回答。

但是 \(cnt_{[r+1,n],lower,a_{l'}}\) 不太好求,考虑到莫队有 \(O(n\sqrt{m})\) 次指针移动的过程,所以对应的这个查询也有 \(O(n\sqrt{m})\) 次。所以我们必须要有一种能够 \(O(1)\) 回答这个询问的办法。

考虑莫队二次离线,把莫队指针移动产生的 \(O(n\sqrt{m})\) 个形如 \(cnt_{[r+1,n],lower,a_{l'}}\) 的询问再次离线下来。这里需要注意一下,lxl 比较毒瘤,卡了线性空间。所以我们需要把 \(O(n\sqrt{m})\) 个询问想办法用 \(O(n+m)\) 的空间存储。发现我们的指针一次移动是单向的,形成了一段区间,所以没必要把所有指针移动都存下来,只需要存移动的起点和终点就可以了。

发现,虽然有 \(O(n\sqrt{m})\) 次查询,但就只有 \(O(n)\) 次插入操作。这里用到了一个叫做“根号平衡”的思想。我们只需要一种能够 \(O(\sqrt{n})\) 插入,\(O(1)\) 查询的数据结构就可以了。

值域分块,查询比一个数小的数的个数。其实就是求区间和。求区间和就想到了前缀和,插入一个数其实就是对若干个前缀和做贡献。

分成块上前缀和和块内前缀和两部分,因为块长是根号,所以暴力对两部分做贡献加一就可以。一次插入复杂度 \(O(\sqrt{n})\)。

于是我们就求出了中间所有需要的数据。把他们再组织起来就可以了。

考虑到,莫队中,每次指针移动,都是产生了一个对当前询问的贡献,也就是 \(delta\)。所以我们只需要把每次的 \(delta\) 累加到当前询问下就可以了。而相邻两次询问,也是基于上一次询问的数据,加上了这一次询问指针产生的若干 \(delta\) 贡献。所以对所有的询问再做一次前缀和就行。

最后把莫队排完序后的询问还原成原询问顺序输出就可以。

代码

#include<bits/stdc++.h>
using namespace std;
/*====================*/
#define endl "\n"
/*====================*/
typedef long long lnt;
/*====================*/
const int N = 1e5 + 10;
const int M = 1e5 + 10;
/*====================*/
const int INF = 0X3F3F3F3F;
/*====================*/
int n, m, a[N];
/*====================*/
template<class Type>
class C_FenwickTree
{
private:
	int n; vector<int>tree;
	/*====================*/
	int lowbit(int x) { return x & (-x); }
public:
	void Init(int n)
	{
		this->n = n; tree.assign(n + 1, Type());
	}
	void Add(int pos, Type d)
	{
		while (pos <= n)
		{
			tree[pos] += d;
			pos += lowbit(pos);
		}
	}
	Type Ask(int pos)
	{
		Type res = Type();
		while (pos)
		{
			res += tree[pos];
			pos -= lowbit(pos);
		}
		return res;
	}
	Type Ask(int l, int r)
	{
		if (l > r)return Type();
		Type res = Type(); l--;
		while (r > l)res += tree[r], r -= lowbit(r);
		while (l > r)res -= tree[l], l -= lowbit(l);
		return res;
	}
};
/*====================*/
int preupper[N], suflower[N];
/*====================*/
const int MO_S = 300;
/*====================*/
struct Query1
{
	int l, r, idx;
	Query1(int _l = 0, int _r = 0, int _idx = 0)
	{
		l = _l, r = _r, idx = _idx;
	}
	friend bool operator<(const Query1& a, const Query1& b)
	{
		return (a.l / MO_S == b.l / MO_S) ? (((a.l / MO_S) & 1) ? (a.r > b.r) : (a.r < b.r)) : (a.l < b.l);
	}
}query[M];
/*====================*/
lnt moans[M];
/*====================*/
struct Query2
{
	int l, r, sign, idx;
	Query2(int _l = 0, int _r = 0, int _sign = 0, int _idx = 0)
	{
		l = _l, r = _r, sign = _sign, idx = _idx;
	}
};
vector<Query2>sufquery[N];
vector<Query2>prequery[N];
/*====================*/
const int Block_S = 300;
const int Block_B = N / Block_S + 10;
/*====================*/
int belong[N];
struct Block
{
	int l, r;
	Block(void)
	{
		l = r = 0;
	}
}block[Block_B];
/*====================*/
void Solve(void)
{
	do
	{
		cin >> n >> m;
		for (int i = 1; i <= n; ++i)
		{
			cin >> a[i];
		}
		for (int i = 1; i <= m; ++i)
		{
			int l, r; cin >> l >> r;
			query[i] = Query1(l, r, i);
		}
	} while (false);//读入

	do
	{
		vector<int>lib;
		for (int i = 1; i <= n; ++i)
		{
			lib.push_back(a[i]);
		}
		sort(lib.begin(), lib.end());
		lib.erase(unique(lib.begin(), lib.end()), lib.end());
		for (int i = 1; i <= n; ++i)
		{
			a[i] = lower_bound(lib.begin(), lib.end(), a[i]) - lib.begin() + 1;
		}
	} while (false);//离散化

	do
	{
		C_FenwickTree<int>tree; tree.Init(n);
		for (int i = 1; i <= n; ++i)
		{
			preupper[i] = tree.Ask(a[i] + 1, n); tree.Add(a[i], 1);
		}
	} while (false);//预处理前缀upper

	do
	{
		C_FenwickTree<int>tree; tree.Init(n);
		for (int i = n; i >= 1; --i)
		{
			suflower[i] = tree.Ask(1, a[i] - 1); tree.Add(a[i], 1);
		}
	} while (false);//预处理后缀lower

	do
	{
		sort(query + 1, query + 1 + m);
		int l = 1, r = 0;
		for (int i = 1; i <= m; ++i)
		{
			if (query[i].l < l)sufquery[r + 1].push_back(Query2(query[i].l, l - 1, -1, i));
			while (query[i].l < l)moans[i] += suflower[--l];
			if (r < query[i].r)prequery[l - 1].push_back(Query2(r + 1, query[i].r, -1, i));
			while (r < query[i].r)moans[i] += preupper[++r];
			if (l < query[i].l)sufquery[r + 1].push_back(Query2(l, query[i].l - 1, +1, i));
			while (l < query[i].l)moans[i] -= suflower[l++];
			if (query[i].r < r)prequery[l - 1].push_back(Query2(query[i].r + 1, r, +1, i));
			while (query[i].r < r)moans[i] -= preupper[r--];
		}
	} while (false);//一次离线莫队

	do
	{
		for (int i = 1; i <= n; ++i)
		{
			belong[i] = i / Block_S + 1;
			if (block[belong[i]].l == 0)
			{
				block[belong[i]].l = i;
			}
			block[belong[i]].r = i;
		}
	} while (false);//预处理分块

	do
	{
		//查询[1,idx]中有多少数大于a[l~r]
		vector<int>cnt1(n + 10, 0), cnt2(belong[n] + 10, 0);
		for (int i = 1; i <= n; ++i)
		{
			//插入a[i]
			for (int j = a[i]; j >= block[belong[a[i]]].l; --j)cnt1[j]++;
			for (int j = belong[a[i]]; j >= belong[1]; --j)cnt2[j]++;
			for (int j = 0; j < prequery[i].size(); ++j)
			{
				int l = prequery[i][j].l;
				int r = prequery[i][j].r;
				for (int k = l; k <= r; ++k)
				{
					//查询大于a[k]的个数
					if (a[k] + 1 <= n)
					{
						moans[prequery[i][j].idx] += prequery[i][j].sign * (cnt1[a[k] + 1] + cnt2[belong[a[k] + 1] + 1]);
					}
				}
			}
		}
	} while (false);//二次离线莫队处理prequery

	do
	{
		//查询[idx,n]中有多少数小于a[l~r]
		vector<int>cnt1(n + 10, 0), cnt2(belong[n] + 10, 0);
		for (int i = n; i >= 1; --i)
		{
			//插入a[i]
			for (int j = a[i]; j <= block[belong[a[i]]].r; ++j)cnt1[j]++;
			for (int j = belong[a[i]]; j <= belong[n]; ++j)cnt2[j]++;
			for (int j = 0; j < sufquery[i].size(); ++j)
			{
				int l = sufquery[i][j].l;
				int r = sufquery[i][j].r;
				for (int k = l; k <= r; ++k)
				{
					//查询小于a[k]的个数
					if (a[k] - 1 >= 1)
					{
						moans[sufquery[i][j].idx] += sufquery[i][j].sign * (cnt1[a[k] - 1] + cnt2[belong[a[k] - 1] - 1]);
					}
				}
			}
		}
	} while (false);//二次离线莫队处理sufquery

	do
	{
		for (int i = 1; i <= m; ++i)
		{
			moans[i] += moans[i - 1];
		}
		vector<lnt>ans(m + 1, 0);
		for (int i = 1; i <= m; ++i)
		{
			ans[query[i].idx] = moans[i];
		}
		for (int i = 1; i <= m; ++i)
		{
			cout << ans[i] << endl;
		}
	} while (false);//处理输出答案
}
/*====================*/
int main()
{
#ifndef ONLINE_JUDGE
	freopen("IN.txt", "r+", stdin);
#endif
	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
	int T = 1; //cin >> T;
	while (T--)Solve();
	return 0;
}

标签:lower,idx,int,Ynoi,sqrt,II,cnt,莫队
From: https://www.cnblogs.com/ProtectEMmm/p/18425730

相关文章

  • Day 20 回溯法part02| LeetCode 39. 组合总和 ,40.组合总和II,131.分割回文串
    39.组合总和39.组合总和classSolution{publicList<List<Integer>>res=newArrayList<>();publicList<Integer>path=newLinkedList<>();publicList<List<Integer>>combinationSum(int[]cand......
  • Day 21 回溯法part03| LeetCode 93. 复原 IP 地址,78.子集,90.子集II
    93.复原IP地址93.复原IP地址classSolution{List<String>res=newArrayList<>();publicList<String>restoreIpAddresses(Strings){backtracking(s,0,0);returnres;}voidbacktrack......
  • The 2024 ICPC Asia EC Regionals Online Contest (II) - Problem H. Points Selectio
    注意到如果$\text{query}(a,b,c)$为真,那么$\text{query}(\geqa,\geqb,c)$一定为真。从小到大枚举询问中$a$的值,按横坐标从小到大依次加入每个点,维护$f_c$表示最小的$b$满足$\text{query}(a,b,c)$为真。假设当前正在加入点$(x,y,w)$,有$f_{(c+w)\bmodn}=\min(f_{......
  • The 2024 ICPC Asia EC Regionals Online Contest (II) - Problem B. Mountain Bookin
    从$1$到$m$依次考虑每个日期。假设当前正在考虑第$i$天,那么只有第$i$天来访的游客以及指定第$i$天的查询是有用的。将这些游客和查询都提取出来,通过Kruskal重构树可以很方便地在$O(n\logn)$的时间内计算出这些查询的答案。不幸的是,本题还有加边删边操作,无法轻易地......
  • IIS8.0无法加载asp.net程序的解决方案
    1.更改系统文件machine.config文件,它位于C:\WINNT\Microsoft.NET\下面<configProtectedDatadefaultProvider="RsaProtectedConfigurationProvider">    <providers>      <addname="RsaProtectedConfigurationProvider"type="......
  • The 2024 ICPC Asia EC Regionals Online Contest (II)
    目录写在前面F签到A枚举J贪心I构造,二进制L数学,三分G数学,辗转相除E结论,最短路写在最后写在前面补题地址:https://codeforces.com/gym/105358。以下按个人向难度排序。妈的7题秒完剩下的题感觉没一个能做的。F签到#include<bits/stdc++.h>#definelllonglongcon......
  • Ynoi 合集
    注:无特殊说明的情况下,\(m\)和\(q\)等都视为与\(n\)同阶。[Ynoi2010]Fusiontree感觉很具有启发性的题目。首先我们对于每一个点维护其儿子所组成的01-trie。父亲的操作就单独处理即可。那么我们的目标其实很明确:就是执行一个对字典树内所有元素加\(1\)的操作。而这个......
  • iis服务器帝国cms7.5编辑器不能使用解决办法
    在IIS服务器上使用帝国CMS7.5时,如果编辑器不能正常使用,可能涉及多个方面的问题,包括文件权限、配置文件、依赖库等。下面是一些具体的解决办法:1.检查文件和目录权限确保帝国CMS的所有必要文件和目录具有正确的权限。步骤:检查e/data目录及其子目录:使用IISManager或其他工......
  • JAVA学习-练习试用Java实现“不同的二叉搜索树 II”
    问题:给定一个整数n,请生成并返回所有由n个节点组成且节点值从1到n互不相同的不同二叉搜索树。可以按任意顺序返回答案。示例1:输入:n=3输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]示例2:输入:n=1输出:[[1]]提示:1<=n......
  • leetcode刷题day22|回溯算法Part01( 77. 组合 、216. 组合总和 III、17.电话号码的字母
    前言:回溯是递归的副产品,只要有递归就会有回溯,回溯函数也就是递归函数。回溯是暴力穷举解法,效率并不高。但一些问题只能使用回溯来解决。回溯法,一般可以解决如下几种问题:组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一......