题解 | #[HEOI2012]采花#

[HEOI2012]采花

https://ac.nowcoder.com/acm/problem/20545

链接:https://ac.nowcoder.com/acm/contest/26896/1036
来源:牛客网

题目描述

萧芸斓是Z国的公主,平时的一大爱好是采花。
今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。
花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 1−c 表示),且花是排成一排的,以便于公主采花。
公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴!同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。
由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 m 个行程,然后一一向你询问公主能采到多少朵花(她知道你是编程高手,定能快速给出答案!),最后会选择令公主最高兴的行程(为了拿到更多奖金!)。

输入描述:

第一行三个空格隔开的整数 n、c以及 m。
接下来一行 n 个空格隔开的整数,每个数在[1, c]间,第 i 个数表示第 i 朵花的颜色。
接下来 m 行每行两个空格隔开的整数 lr(l≤r),表示女仆安排的行程为公主经过第 l 到第 r 朵花进行采花。

输出描述:

m 行,每行一个整数,第 i 个数表示公主在女仆的第 i 个行程中能采到的花的颜色数。

题型:

树状数组--区间修改与单点查询

思路:

这一道题难度中等偏下,在想出来需要维护的东西之后就更简单了(P.S:这道题注意输出的是采到的花的颜色数而不是数量!和题目中的“能采到多少朵花”我只能说牛头不对马嘴,所以雨巨当时讲的就是能采到多少朵花,而不是采到的花的颜色数不过雨巨对于能采到多少朵花的分析,其实是和采到的花的颜色数的分析是类似的
下面是分析的过程
我们可以先随意捏造一组花的数据(为了直观),如:
然后最关键的部分来了:如何能够任意选择某个区间,就能直接获得采到的花的颜色数呢?
如果仔细思考过这个问题,会发现,要想每次输入一个区间,就能输出一个值是不合理的,至少在时间复杂度上是不合理的,因为树状数组没办法用区间加减来维护这种值
所以,考虑先把所有的区间询问存储起来,排序之后统一处理
至于区间如何排序可以先放一放,先看看这些区间的答案是如何求出来的,这也是本题的难点
对于
我们设last[i]为与第i个元素相同的上一个元素所在的位置,lastst[i]为与第i个元素相同的上一个的上一个元素所在的位置,初始都设为0,再设一个tong[i]来存储第i个元素出现的次数,最后设一个tree[i]表示从此处开始,到当前的右区间的采到的花的颜色数(听起来有些拗口,看下面模拟的过程应该能够看懂)
则初始有
我们以右区间为基准,即将右区间从第一个位置扫到最后一个位置
当右区间指向第一个位置 1 时,有

当右区间指向第二个位置 3 时,有

当右区间指向第三个位置 2 时,有

重要的来了!当右区间指向第四个位置 1 时,由于1之前已经出现过,说明开始存在区间右界为4的区间,使得采到的花的颜色数不为0,此时可以得出,需要对[lastst[1]+1~last[1]](即[1,1])之间的区间进行+1操作(注意!流程应该为元素先放入桶中,如果桶内元素>=2,则先执行区间+1操作,再更新last和lastst,执行完上述操作后,有

此时观察一下这个tree数组的值,我们可以看到,tree[i]所对应的值就是区间[i,当前右区间位置]的值(如区间[1,4]的值为1,[2,4],[3,4],[4,4]的值都为0)
同理,当右区间指向第五个位置 3 时

同理,观察一下这个tree数组的值,tree[i]所对应的值就是区间[i,当前右区间位置]的值(如区间[1,5]的值为2,[2,5]的值为1,[3,5],[4,5],[5,5]的值都为0)
为了突出重点,中间的模拟步骤部分省略,直接跳到关键的部分
自行对比一下,下面是当右区间指向第七个位置 4 时的值

现在,当右区间指向第八个位置 3 时,发现3出现了第三次,但是操作仍旧与之前的相同,即对[lastst[3]+1~last[3]](即[3,5])之间的区间进行+1操作

最后再放一张当右区间指向最后一个位置时的图片,自行对比是否正确

以上就是模拟过程了,可以发现,代码主要就是区间修改(+1)和单点查询(查询左区间对应的的值),因此代码的自定义函数部分其实就是裸的板子
模拟过程结束了,回到区间排序这个问题上来,我们也不难想出,区间排序应该是以右区间升序排序
这就是大致上的思路过程,其实思路有了(知道应该维护什么了)还是很简单的
(P.S.:昨天刚说写了一篇目前写的最长的题解,今天似乎就更长了...,不过能够锻炼树状数组的构造思维还是很开心的一件事~)

代码:

#include<bits/stdc++.h>
#define int long long int
using namespace std;
const int N=2097153;
int tree[N],a[N],last[N],lastst[N],tong[N];
int n,m,c;
struct node{
	int id,L,R,ans;
}d[N];
int lowbit(int x){
	return x&(-x);
}

void add(int i,int val){
	while(i<=n){
		tree[i]+=val;
		i+=lowbit(i);
	}
}

int sum(int i){
	int ans=0;
	while(i>0){
		ans+=tree[i];
		i-=lowbit(i);
	}
	return ans;
}

int cmp(struct node a,struct node b){
	return a.R<b.R;
}

int cmp2(struct node a,struct node b){
	return a.id<b.id;
}

signed main(){
	scanf("%lld%lld%lld",&n,&c,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++){
		d[i].id=i;
		scanf("%lld%lld",&d[i].L,&d[i].R);
	}
	sort(d+1,d+1+m,cmp); //按照右区间升序排序
	int pos=1;
	for(int i=1;i<=n;i++){
		tong[a[i]]++;
		if(tong[a[i]]>=2){ //元素出现次数>=2次,执行区间+1操作
			add(lastst[a[i]]+1,1);
			add(last[a[i]]+1,-1);
		}
		lastst[a[i]]=last[a[i]]; //更新lastst
		last[a[i]]=i; //更新last
		while(d[pos].R==i && pos<=m){ //获取答案并且存储在对应区间内
			d[pos].ans=sum(d[pos].L);
			pos++;
		}
	}
	sort(d+1,d+1+m,cmp2); //按照输入顺序升序排序
	for(int i=1;i<=m;i++){
		printf("%lld\n",d[i].ans);
	}
	return 0;
}


这是本人对于算法入门班共十三章习题的题解与个人的一些感悟,本人写题解的目的是对于某些经典的/陌生的/无从下手的/思维方向错误的题目加深印象。 希望自己能够在学习算法的路上走得更远,今天也是努力学习的一天!

全部评论

相关推荐

重生我想学测开:嵌入式的问题,我准备入行京东外卖了
点赞 评论 收藏
分享
避坑恶心到我了大家好,今天我想跟大家聊聊我在成都千子成智能科技有限公司(以下简称千子成)的求职经历,希望能给大家一些参考。千子成的母公司是“同创主悦”,主要经营各种产品,比如菜刀、POS机、电话卡等等。听起来是不是有点像地推销售公司?没错,就是那种类型的公司。我当时刚毕业,急需一份临时工作,所以在BOSS上看到了千子成的招聘信息。他们承诺无责底薪5000元,还包住宿,这吸引了我。面试的时候,HR也说了同样的话,感觉挺靠谱的。于是,我满怀期待地等待结果。结果出来后,我通过了面试,第二天就收到了试岗通知。试岗的内容就是地推销售,公司划定一个区域,然后你就得见人就问,问店铺、问路人,一直问到他们有意向为止。如果他们有兴趣,你就得摇同事帮忙推动,促进成交。说说一天的工作安排吧。工作时间是从早上8:30到晚上18:30。早上7点有人叫你起床,收拾后去公司,然后唱歌跳舞(销售公司都这样),7:55早课(类似宣誓),8:05同事间联系销售话术,8:15分享销售技巧,8:30经理训话。9:20左右从公司下市场,公交、地铁、自行车自费。到了市场大概10点左右,开始地推工作。中午吃饭时间大约是12:00,公司附近的路边盖饭面馆店自费AA,吃饭时间大约40分钟左右。吃完饭后继续地推工作,没有所谓的固定中午午休时间。下午6点下班后返回公司,不能直接下班,需要与同事交流话术,经理讲话洗脑。正常情况下9点下班。整个上班的一天中,早上到公司就是站着的,到晚上下班前都是站着。每天步数2万步以上。公司员工没有自己的工位,百来号人挤在一个20平方米的空间里听经理洗脑。白天就在市场上奔波,公司的投入成本几乎只有租金和工资,没有中央空调。早上2小时,晚上加班2小时,纯蒸桑拿。没有任何福利,节假日也没有3倍工资之类的。偶尔会有冲的酸梅汤和西瓜什么的。公司的晋升路径也很有意思:新人—组长—领队—主管—副经理—经理。要求是业绩和团队人数,类似传销模式,把人留下来。新人不能加微信、不能吐槽公司、不能有负面情绪、不能谈恋爱、不能说累。在公司没有任何坐的地方,不能依墙而坐。早上吃早饭在公司外面的安全通道,未到上班时间还会让你吃快些不能磨蹭。总之就是想榨干你。复试的时候,带你的师傅会给你营造一个钱多事少离家近的工作氛围,吹嘘工资有多高、还能吹自己毕业于好大学。然后让你早点来公司、无偿加班、抓住你可能不会走的心思进一步压榨你。总之,大家在找工作的时候一定要擦亮眼睛,避免踩坑!———来自网友
qq乃乃好喝到咩噗茶:不要做没有专业门槛的工作
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务