洛谷P1972 [SDOI2009]HH的项链 基础莫队+卡常/线段树离线
洛谷P1972 [SDOI2009]HH的项链
标签
- 基础莫队
- 卡常
- 线段树离线
前言
- 我的csdn和博客园是同步的,欢迎来访danzh-博客园~
- 这才是最裸的莫队惹。然而数据加强了,莫队需要吸氧+卡常才能过,这篇文章简单讲讲卡常,主要讲线段树
简明题意
- 给一个序列,每次询问你区间[L,R]中有多少种不同的数字
思路
- 这才是最裸的莫队惹。然而数据加强了,莫队需要吸氧+卡常才能过,然后卡常的相关操作写到总结里吧~
- 然后还有一种用权值线段树维护的不那么暴力的方法。(很巧妙的鸭)
 ------------------------------------------------------分鸽线------------------------------------------------------
- 首先,我们看这样一组样例:
\[1,2,4,5,2,4,2
\]
- 我们把相同的数中的最后一个置为1
\[1,2,4,5,2,4,2
\]
\[1,0,0,1,0,1,1
\]
- 因为每个数只会产生一个1,所以现在要求区间[L,R]中不同的数的个数,就直接统计1的个数就行了。
- 仔细想想,上面直接统计1的个数是不对的。比如查询[3,3],显然按上面的方法查到的是0,而实际却是1。为什么?实际上,上面的统计方法是对的,只是,只有当查询的右端点是最后一个数,也就是右端点r=7时,才是对的(仔细想想为什么),而左端点可以是任意的。
- 显然当右端点固定是时,上面的东西是可以用线段树维护的,维护一下区间的和就可以了。
- 但是每次询问右端点都不一样。怎么办呢?可以借助主席树的思想了,每移动一次右端点,就更新一下当前的线段树,保证线段树维护的是对于当前r的区间和。
- 所以说应该离线,把询问按照右端点排序,每次右端点增大,就先更新一下线段树,然后再查询。
- 最后说说更新线段树,比如所要更新i=5。那么应该把01序列中的第5个改为1,然后再把第5个数2的前面的一个2,在线段树中将他改为0.所以说我们需要预处理每个数a[i]的前一个和他相同的数的下表,就这样这题就能AC
注意事项
- 无
总结
莫队部分
- 快读,快写
inline int read()
{
	int ans = 0;
	char r = getchar(); bool neg = false;
	while (r<'0' || r>'9') { if (r == '-')neg = true; r = getchar(); }
	while (r >= '0'&&r <= '9') { ans = ans * 10 + r - '0'; r = getchar(); }
	return (neg) ? -ans : ans;
}
void write(int x) {
	if (x < 0) { putchar('-'); x = -x; }
	if (x > 9)write(x / 10);
	putchar(x % 10 + '0');
}//write不带换行符,需要自己putchar('\n')
- 奇偶性排序
bool operator < (const Query& a)const
{
	return pos[l] ^ pos[a.l] ? pos[l] < pos[a.l] : pos[l] & 1 ? r<a.r : r>a.r;
}
- 分块的大小
int len =  n / sqrt(n * 2 / 3);
线段树部分
- 要随时能想到这种线段树离线的方法~
AC代码
莫队
#pragma GCC optimize(2)
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
	int ans = 0;
	char r = getchar(); bool neg = false;
	while (r<'0' || r>'9') { if (r == '-')neg = true; r = getchar(); }
	while (r >= '0'&&r <= '9') { ans = ans * 10 + r - '0'; r = getchar(); }
	return (neg) ? -ans : ans;
}
void write(int x) {
	if (x < 0) { putchar('-'); x = -x; }
	if (x > 9)write(x / 10);
	putchar(x % 10 + '0');
}
const int maxn = 1e6 + 10;
int pos[maxn];
struct Query
{
	int l, r, id;
	bool operator < (const Query& a)const
	{
		return pos[l] ^ pos[a.l] ? pos[l] < pos[a.l] : pos[l] & 1 ? r<a.r : r>a.r;
	}
};
Query query[maxn];
int n, q, a[maxn];
int ans, cnt[maxn];
int ans0[maxn];
void solve()
{
	scanf("%d", &n);
	int len =  n / sqrt(n * 2 / 3);
	for (int i = 1; i <= n; i++)
		a[i] = read(), pos[i] = (i - 1) / len + 1;
	scanf("%d", &q);
	for (int i = 1; i <= q; i++)
		query[i].l = read(), query[i].r = read(), query[i].id = i;
	sort(query + 1, query + 1 + q);
	int l = 1, r = 0;
	for (int i = 1; i <= q; i++)
	{
		int L = query[i].l, R = query[i].r;
		while (l < L) if (cnt[a[l++]]-- == 1) ans--;
		while (l > L)if (cnt[a[--l]]++ == 0) ans++;
		while (r < R) if (cnt[a[++r]]++ == 0) ans++;
		while (r > R) if (cnt[a[r--]]-- == 1) ans--;
		ans0[query[i].id] = ans;
	}
	for (int i = 1; i <= q; i++)
		write(ans0[i]), putchar('\n');
}
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}
线段树
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 10;
struct Query
{
	int l, r, id;
	bool operator < (const Query& a)const
	{
		return r < a.r;
	}
};Query query[maxn];
struct Node
{
	int l, r, sum;
}; Node tree[maxn * 4];
void build(int o, int l, int r)
{
	tree[o].l = l, tree[o].r = r;
	if (l == r)
		return;
	int mid = (l + r) / 2;
	build(o * 2, l, mid);
	build(o * 2 + 1, mid + 1, r);
}
void change(int o, int x, int type)
{
	if (tree[o].l == tree[o].r)
	{
		tree[o].sum = type;
		return;
	}
	
	int mid = (tree[o].l + tree[o].r) / 2;
	if (x <= mid)
		change(o * 2, x, type);
	else
		change(o * 2 + 1, x, type);
	tree[o].sum = tree[o * 2].sum + tree[o * 2 + 1].sum;
}
int ask(int o, int l, int r)
{
	if (tree[o].l == l && tree[o].r == r)
		return tree[o].sum;
	int mid = (tree[o].l + tree[o].r) / 2;
	if (r <= mid)
		return ask(o * 2, l, r);
	else if (l > mid)
		return ask(o * 2 + 1, l, r);
	return ask(o * 2, l, mid) + ask(o * 2 + 1, mid + 1, r);
}
int n, q, a[maxn];
int ans0[maxn];
int las[maxn], pos[maxn];
void solve()
{
	build(1, 1, maxn - 10);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), las[i] = pos[a[i]], pos[a[i]] = i;
	scanf("%d", &q);
	for (int i = 1; i <= q; i++)
		scanf("%d%d", &query[i].l, &query[i].r), query[i].id = i;
	sort(query + 1, query + 1 + q);
	int las_r = 1;
	for (int i = 1; i <= q; i++)
	{
		int l = query[i].l, r = query[i].r;
		for (int i = las_r; i <= r; i++)
		{
			if (las[i] != 0)
				change(1, las[i], 0);
			change(1, i, 1);
		}
			
		ans0[query[i].id] = ask(1, l, r);
		las_r = r + 1;
	}
	for (int i = 1; i <= q; i++)
		printf("%d\n", ans0[i]);
}
int main()
{
	freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}
作者:danzh
QQ:1244536605
CSDN(和博客园同步):https://blog.csdn.net/weixin_42431507
-----------------------------------------------------------------------------------------------
朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其
它人怎么样,我们也能够保持自己的本色走下去。
—clj

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号