【洛谷4396/BZOJ3236】[AHOI2013]作业(莫队+分块/树状数组/线段树)

3 篇文章 0 订阅
2 篇文章 0 订阅

题目:

洛谷4396

BZOJ3236(权限)

这题似乎BZOJ上数据强一些?

分析:

这题真的是……一言难尽

发现题面里没说权值的范围,怕出锅就写了离散化。后来经过面向数据编程(以及膜神犇代码)知道最大权值 1 e 5 1e5 1e5(下文用 M M M表示最大权值。注意如果没有这个限制,把所有数的权值和询问中提到的权值一起离散化后 M M M也可以达到 n + 2 m = 2.1 e 6 n+2m=2.1e6 n+2m=2.1e6),如果没这个限制我很想知道正解应该怎么写……下面再细说

首先看到这种询问某个区间内在某个区域内值的数量的题,显然能想到把询问离线下来,用个什么数据结构维护莫队。

然后我大概脑补了一下,这个“数据结构”似乎可以用权值线段树?每个区间维护 s u m sum sum n u m num num对应两种询问。对于叶子节点,若 s u m sum sum 0 0 0 n u m num num 0 0 0,否则 n u m num num 1 1 1。对于非叶子节点, s u m sum sum n u m num num都是分别是两儿子的 s u m sum sum/ n u m num num之和。这样做正确性是显然的,复杂度 O ( m n × l o g 2 M ) O(m\sqrt n \times log_2M) O(mn ×log2M),大概是 1 e 6 × 1 e 5 × 21 ≈ 6.64 e 9 1e6\times \sqrt{1e5}\times 21\approx 6.64e9 1e6×1e5 ×216.64e9,过个鬼哦。

树状数组做法类似。维护两个树状数组 s u m sum sum n u m num num,同样对应两个询问。给 a a a加的时候如果原本 s u m [ a ] sum[a] sum[a] 0 0 0就给 n u m [ a ] num[a] num[a],给 a a a减的时候如果原本 s u m [ a ] sum[a] sum[a] 1 1 1就给 n u m [ a ] num[a] num[a]减。复杂度和线段树是一样的,所以也过不去。

然后没想到竟然分块能过……把权值分块维护, b l s u m blsum blsum b l n u m blnum blnum表示块内两种询问的答案, n u m [ a ] num[a] num[a]表示 a a a的出现次数,维护和查询方式详见代码。

在我僵化的思维里 l o g 2 n log_2n log2n的树状数组和线段树比 n \sqrt n n 的分块要优,但是这道题里分块的修改是 O ( 1 ) O(1) O(1),查询是 O ( M ) O(\sqrt M) O(M )。而查询不需要乘上莫队的 O ( n ) O(\sqrt n) O(n ),所以最终复杂度是 O ( m ( n + M ) ) O(m (\sqrt n+\sqrt M)) O(m(n +M )),大概是 1 e 6 × ( 1 e 5 + 2.1 e 6 ) = 1.77 e 9 1e6\times(\sqrt{1e5} +\sqrt{2.1e6})=1.77e9 1e6×(1e5 +2.1e6 )=1.77e9,还是悬。如果权值最大是 1 e 5 1e5 1e5还是能跑过去的。

代码:

(线段树和树状数组没有AC)

线段树: 161 161 161行,本地 83 83 83s,内存 100.4 100.4 100.4MB,BZOJ上TLE。下方代码中省略部分和树状数组基本一致

namespace Segment_Tree
{
	struct node
	{
		int sum, num;
	}tree[(N + (M << 1)) << 2];
	pii operator + (const pii &a, const pii &b)
	{
		return make_pair(a.first + b.first, a.second + b.second);
	}
	pii operator += (pii &a, const pii &b)
	{
		return a = a + b;
	}
	void update(const int rot)
	{
		tree[rot].sum = tree[rot << 1].sum + tree[rot << 1 | 1].sum;
		tree[rot].num = tree[rot << 1].num + tree[rot << 1 | 1].num;
	}
	void change(const int rot, const int lt, const int rt, const int pos, const int x)
	{
		if (lt == rt)
		{
			tree[rot].sum += x;
			tree[rot].num = (tree[rot].sum ? 1 : 0);
			return;
		}
		int mid = (lt + rt) >> 1;
		if (pos <= mid)
			change(rot << 1, lt, mid, pos, x);
		else
			change(rot << 1 | 1, mid + 1, rt, pos, x);
		update(rot);
	}
	pii query(const int rot, const int lt, const int rt, const int ls, const int rs)
	{
		if (ls <= lt && rt <= rs)
			return make_pair(tree[rot].sum, tree[rot].num);
		int mid = (lt + rt) >> 1;
		pii ans = make_pair(0, 0);
		if (ls <= mid)
			ans += query(rot << 1, lt, mid, ls, rs);
		if (rs > mid)
			ans += query(rot << 1 | 1, mid + 1, rt, ls, rs);
		return ans;
	}
}

树状数组: 153 153 153行,本地 23 23 23s(一样的复杂度比线段树快近 3 3 3倍,结果BZOJ上还是TLE什么鬼),内存 37.0 37.0 37.0MB

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

namespace zyt
{
	template<typename T>
	inline void read(T &x)
	{
		char c;
		bool f = false;
		x = 0;
		do
			c = getchar();
		while (c != '-' && !isdigit(c));
		if (c == '-')
			f = true, c = getchar();
		do
			x = x * 10 + c - '0', c = getchar();
		while (isdigit(c));
		if (f)
			x = -x;
	}
	template<typename T>
	inline void write(T x)
	{
		static char buf[20];
		char *pos = buf;
		if (x < 0)
			putchar('-'), x = -x;
		do
			*pos++ = x % 10 + '0';
		while (x /= 10);
		while (pos > buf)
			putchar(*--pos);
	}
	inline void write(const pair<int, int> &a)
	{
		write(a.first), putchar(' '), write(a.second);
	}
	const int N = 1e5 + 10, M = 1e6 + 10;
	int n, m, arr[N], belong[N];
	typedef pair<int, int> pii;
	pii ans[M];
	struct _ask
	{
		int l, r, a, b, id;
		bool operator < (const _ask &b) const
		{
			return belong[l] == belong[b.l] ? r < b.r : l < b.l;
		}
	}ask[M];
	int cnt, tmp[N + (M << 1)];
	class Tree_Array
	{
	private:
		int data[N];
		inline int lowbit(const int x)
		{
			return x & (-x);
		}
	public:
		inline void add(int a, const int x)
		{
			while (a <= cnt)
				data[a] += x, a += lowbit(a);
		}
		inline int query(int a)
		{
			int ans = 0;
			while (a)
				ans += data[a], a -= lowbit(a);
			return ans;
		}
		inline int query(const int l, const int r)
		{
			return query(r) - query(l - 1);
		}
	}sum, num;
	void update(const int pos, const int x)
	{
		if (x == 1)
		{
			if (!sum.query(pos, pos))
				num.add(pos, 1);
			sum.add(pos, 1); 
		}
		if (x == -1)
		{
			sum.add(pos, -1);
			if (!sum.query(pos, pos))
				num.add(pos, -1);
		}
	}
	void Mo_Algorithm()
	{
		int l = 1, r = 1;
		update(arr[1], 1);
		for (int i = 1; i <= m; i++)
		{
			while (l < ask[i].l)
				update(arr[l++], -1);
			while (l > ask[i].l)
				update(arr[--l], 1);
			while (r < ask[i].r)
				update(arr[++r], 1);
			while (r > ask[i].r)
				update(arr[r--], -1);
			if (ask[i].a > ask[i].b)
				ans[ask[i].id] = make_pair(0, 0);
			else
				ans[ask[i].id] = make_pair(sum.query(ask[i].a, ask[i].b), num.query(ask[i].a, ask[i].b));
		}
	}
	int work()
	{
		int block;
		read(n), read(m);
		block = sqrt(n);
		for (int i = 1; i <= n; i++)
		{
			read(arr[i]), tmp[cnt++] = arr[i];
			belong[i] = (i - 1) / block + 1;
		}
		for (int i = 1; i <= m; i++)
		{
			read(ask[i].l), read(ask[i].r), read(ask[i].a), read(ask[i].b);
			ask[i].id = i;
			tmp[cnt++] = ask[i].a, tmp[cnt++] = ask[i].b;
		}
		sort(tmp, tmp + cnt);
		cnt = unique(tmp, tmp + cnt) - tmp;
		for (int i = 1; i <= n; i++)
			arr[i] = upper_bound(tmp, tmp + cnt, arr[i]) - tmp;
		for (int i = 1; i <= m; i++)
		{
			ask[i].a = upper_bound(tmp, tmp + cnt, ask[i].a) - tmp;
			ask[i].b = upper_bound(tmp, tmp + cnt, ask[i].b) - tmp;
		}
		sort(ask + 1, ask + m + 1);
		Mo_Algorithm();
		for (int i = 1; i <= m; i++)
			write(ans[i]), putchar('\n');
		return 0;
	}
}
int main()
{
	return zyt::work();
}

分块: 136 136 136行,本地 8 8 8s,内存 28.5 28.5 28.5MB,BZOJ上AC。这份代码是我知道最大权值 1 e 5 1e5 1e5后写的,所以没有离散化。

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

namespace zyt
{
	template<typename T>
	inline void read(T &x)
	{
		char c;
		bool f = false;
		x = 0;
		do
			c = getchar();
		while (c != '-' && !isdigit(c));
		if (c == '-')
			f = true, c = getchar();
		do
			x = x * 10 + c - '0', c = getchar();
		while (isdigit(c));
		if (f)
			x = -x;
	}
	template<typename T>
	inline void write(T x)
	{
		static char buf[20];
		char *pos = buf;
		if (x < 0)
			putchar('-'), x = -x;
		do
			*pos++ = x % 10 + '0';
		while (x /= 10);
		while (pos > buf)
			putchar(*--pos);
	}
	inline void write(const pair<int, int> &a)
	{
		write(a.first), putchar(' '), write(a.second);
	}
	const int N = 1e5 + 10, M = 1e6 + 10, BL = 510;
	int n, m, arr[N], belong[N];
	typedef pair<int, int> pii;
	pii ans[M];
	struct _ask
	{
		int l, r, a, b, id;
		bool operator < (const _ask &b) const
		{
			return belong[l] == belong[b.l] ? r < b.r : l < b.l;
		}
	}ask[M];
	int blsum[BL], blnum[BL], num[N], begin[BL];
	inline void update(const int pos, const int x)
	{
		blsum[belong[pos]] += x;
		if (x == -1 && !--num[pos])
			--blnum[belong[pos]];
		if (x == 1 && !num[pos]++)
			++blnum[belong[pos]];
	}
	inline pii query(const int l, const int r)
	{
		pii ans = make_pair(0, 0);
		if (belong[r] == belong[l])
		{
			for (int i = l; i <= r; i++)
				if (num[i])
					ans.first += num[i], ++ans.second;
		}
		else
		{
			for (int i = belong[l] + 1; i < belong[r]; i++)
				ans.first += blsum[i], ans.second += blnum[i];
			for (int i = l; i < begin[belong[l] + 1]; i++)
				if (num[i])
					ans.first += num[i], ++ans.second;
			for (int i = begin[belong[r]]; i <= r; i++)
				if (num[i])
					ans.first += num[i], ++ans.second;
		}
		return ans;
	}
	void Mo_Algorithm()
	{
		int l = 1, r = 1, block = sqrt(N), blnum = ceil(N / (double)block);
		for (int i = 1; i < N; i++)
			belong[i] = (i - 1) / block + 1;
		for (int i = 1; i <= blnum; i++)
			begin[i] = (i - 1) * block + 1;
		update(arr[1], 1);
		for (int i = 1; i <= m; i++)
		{
			while (l < ask[i].l)
				update(arr[l++], -1);
			while (l > ask[i].l)
				update(arr[--l], 1);
			while (r < ask[i].r)
				update(arr[++r], 1);
			while (r > ask[i].r)
				update(arr[r--], -1);
			if (ask[i].a > ask[i].b)
				ans[ask[i].id] = make_pair(0, 0);
			else
				ans[ask[i].id] = query(ask[i].a, ask[i].b);
		}
	}
	int work()
	{
		int block;
		read(n), read(m);
		block = sqrt(n);
		for (int i = 1; i <= n; i++)
		{
			read(arr[i]);
			belong[i] = (i - 1) / block + 1;
		}
		for (int i = 1; i <= m; i++)
		{
			read(ask[i].l), read(ask[i].r), read(ask[i].a), read(ask[i].b);
			ask[i].id = i;
		}
		sort(ask + 1, ask + m + 1);
		Mo_Algorithm();
		for (int i = 1; i <= m; i++)
			write(ans[i]), putchar('\n');
		return 0;
	}
}
int main()
{
	return zyt::work();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值