CDOJ 1059 秋实大哥与小朋友(离散化)

本文部分代码及思路来源于:http://www.cnblogs.com/Xiper/p/4470202.html

秋实大哥与小朋友

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

秋实大哥以周济天下,锄强扶弱为己任,他常对天长叹:安得广厦千万间,大庇天下寒士俱欢颜。

所以今天他又在给一群小朋友发糖吃。

他让所有的小朋友排成一行,从左到右标号。在接下去的时间中,他有时会给一段区间的小朋友每人 v 颗糖,有时会问第 x 个小朋友手里有几颗糖。

这对于没上过学的孩子来说实在太困难了,所以你看不下去了,请你帮助小朋友回答所有的询问。

Input

第一行包含两个整数 n m ,表示小朋友的个数,以及接下来你要处理的操作数。

接下来的 m 行,每一行表示下面两种操作之一:

0 l r v : 表示秋实大哥给[l,r]这个区间内的小朋友每人v颗糖

1 x : 表示秋实大哥想知道第x个小朋友手里现在有几颗糖

Output

对于每一个 1   x 操作,输出一个整数,表示第 x 个小朋友手里现在的糖果数目。

Sample input and output

Sample Input Sample Output
3 4
0 1 3 1
1 2
0 2 3 3
1 3 
1
4
树状数组/线段树+离散化
经历了这道题,对于离散化有了具体的理解。离散化,适用于基数很大,但是实际操作时涉及到的数却很小的问题。
首先,离散化是 离线算法。离散化的关键在于,那些没有被涉及到的数,可以去掉。
如这道题里,假如我的区间是[1,10^9]。我有三个操作,把[1,10^6]里的数都加上3,把[10^5,10^9]里的数加上2,查询10,10^4,10^7上的数各是多少。
我们就可以发现,数很大,但是涉及到的数却只有几个。我们涉及到了1,10,10^4,10^5,10^6,10^7,10^9,我们把这几个数排好序,然后做个映射:1->1,10->2,10^4->3,10^5->4,10^6->5,10^7->6,10^9->7。
所以我们的操作相当于:把[1,5]里的数都加上3,把[4,7]里的数都加上2,查询2,3,6上的数各是多少。于是我们发现,大大减小了内存的开销。
那么,为什么可以我们用离散化呢?因为我们发现我们对整个区间里进行了操作,但是,没有被查询到的点都是没用的点。我们完全可以把他们去掉,而对被查询的点没有影响。假如是个区间求和的问题,那么我觉得可能就不能用离散化了吧。
关于离散化,这里贴一篇博文,供大家了解离散化的思想: http://www.cnblogs.com/saltless/archive/2010/11/14/1877171.html
关于树状数组,也贴一篇博文:h ttp://www.hawstein.com/posts/binary-indexed-trees.html
AC代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;
#define maxn 200005
set<int>jud;
long long tr[maxn];
int point[maxn];
//是否可以改掉,只用set而不用再开一个数组?
int size = 0;
struct operate
{
	int op, l, r, v;
	operate(int op, int l, int r, int v)
	{
		this->op = op; this->l = l;
		this->r = r; this->v = v;
	}
};
queue<operate>operque;
void update(int l, int r, int val)
{
	for (int i = r; i > 0; i -= i&(-i))
		tr[i] += val;
	for (int i = l - 1; i > 0; i -= i&(-i))
		tr[i] += (-val);
}
long long query(int idx)
{
	long long sum = 0;
	while (idx <= size)
	{
		sum += tr[idx];
		idx += idx&(-idx);
	}
	return sum;
}
int main()
{
	int l = 0, r = 0, x = 0;
	int n = 0, m = 0, bl = 0, v = 0;
	char op[10] = { '\0' };
	char fst[10] = { '\0' };
	memset(point, 0, sizeof(point));
	memset(tr, 0, sizeof(tr));
	gets(fst);
	sscanf(fst,"%d %d", &n, &m);
	while (m--)
	{
		gets(op);
		if (op[0] == '0')
		{
			sscanf(op, "%d %d %d %d", &bl, &l, &r, &v);
			if (!jud.count(l))
			{
				jud.insert(l);
				point[++size] = l;
			}
			if (!jud.count(r))
			{
				jud.insert(r);
				point[++size] = r;
			}
			operque.push(operate(bl, l, r, v));
		}
		else if (op[0] == '1')
		{
			sscanf(op, "%d %d", &bl, &x);
			if (!jud.count(x))
			{
				jud.insert(x);
				point[++size] = x;
			}
			operque.push(operate(bl, 0, 0, x));
		}
	}
	sort(point + 1, point + 1 + size);
	while (!operque.empty())
	{
		operate temp = operque.front(); operque.pop();
		if (temp.op & 1)
		{
			int pos = lower_bound(point + 1, point + 1 + size, temp.v) - point;
			printf("%lld\n", query(pos));
		}
		else
		{
			int left = lower_bound(point + 1, point + 1 + size, temp.l) - point;
			int right = lower_bound(point + 1, point + 1 + size, temp.r) - point;
			update(left, right, temp.v);
		}
	}
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值