饥饿游戏

(hungry.pas/c/cpp)

【问题描述】

Chanxer饿了,但是囊中羞涩,于是他去参加号称免费吃到饱的“饥饿游戏”。

这个游戏的规则是这样的,举办者会摆出一排 个食物,希望你能够一口就吃完。

然而Chanxer却不这么想,比起数量,他更看重质量,对于没一个食物,都会有一个喜爱值,Chanxer希望能够吃到最美味的那一段。

注意,因为只能吃一口,因此Chanxer只有一次机会,即只能吃连续的一段,因为他的嘴够大,因此无论这一段有多长,他都可以吃到。

不过Chanxer是一个喜新厌旧的男人,对于多个喜爱值相同的食物,他会只算作一次,比如: ,2出现了3次,但只算一次,于是这一段的喜爱值是 。

现在Chanxer对于吃哪一段有很多想法,每一个想法他都想要试试,但是由于只有一次机会,所以他想要你先帮他算算这些他想要吃的段带给他的喜爱值是多少。

【输入】

输入文件名为hungry.in,共 行。

第一行包含一个整数 n。

接下来一行,n个整数,第i个数表示第i个食物的喜爱值a。

接下来一行包含一个整数 m ,表示Chanxer的询问个数。

接下来 m行,每行两个数 l,r,表示Chanxer要询问这个区间的喜爱值【注意是指这个区间内的最优值】。

【输出】

输出文件名为hungry.out,共M行。

每行一个整数表示此次询问的答案。

【输入输出样例】

hungry.in

hungry.out

9

4 -2 -2 3 -1 -4 2 2 -6

3

1 2

1 5

4 9

4

5

3

【数据说明】

对于 的数据满足,n,m<=100;

对于 的数据满足, n,m<=100,000,|ai|<=100,000。

【分析】这题当场最高30分....结果还没有solution ! 无奈只能Orz std的代码了... 然而std的代码高能——使用zkw线段树....

  多亏我当初还是看过一些zkw线段树...于是拉上Zero去黑板上进行模拟运算....画了两棵巨大的线段树...终于半猜半模拟的弄懂了std的思路,爽啊!

  

  正解的具体思路:

  1.发现每次询问[L,R]中的最优值,无非是另一个直接取的一个小的区间 [l,r]满足 l>=L && r<=R。

  2.因为相同的数在一段区间内只会加一次,所以每次在两个相邻的相同的数之间加入这个数。

  

  【经过这两个比较核心的思路】

  STD设计了一个线段树来每次获得最优值。

  每次移动右端点,每次将右端点的值添加进线段树更新一段区间[pre[i]+1,i]的最优值。

  其中每个叶子节点带有两个属性:g[i],f[i]

    g[i]表示从i到当前枚举的最右端点能获得的最优值[但是要求必须选择i这个节点作为左端点],右端点是不固定的。

    f[i]表示从i到当前枚举的最右端点的总和[要求左端点为i,右端点为当前枚举的最右端点],左右端点都是固定的。

    所以可以想到g[i]每次都是由f[i]更新的。

  但是每次这样传到每个叶节点太慢了,于是利用线段树的方法,每次给一段区间添加一个值时,使用lazy_tag,也就是说每个非叶节点多了两个属性: gd[i],fd[i]

  而每个非叶节点还表示这个区间内元素,所以它们也具有一个g[],f[]的意义

    g[i]表示以i能控制的区间内的任意元素作为左端点到当前枚举右端点能获得的最优值 [即i控制区间内g[]的最优值]

    f[i]表示以i能控制的区间内的任意元素作为左端点,当前枚举右端点作为区间右端点能获得的最优值 [即i控制区间内f[]的最优值]

    gd[i]表示对于i能控制的区间内曾经可以添加的最多的值 [也就是没来得及下传的最好的一次 tag]

    fd[i]表示对于i控制的区间内到现在为止累积的值 [也就是每次累加得到的值而已]

    所以也可以想到gd[i]是由fd[i]更新的。

  可以发现每次再添加tag的时候需要下传一些 tag,和线段树一样。

  下传的时候更新下面节点的tag,gd[]由gd[fa]+fd[]更新,fd变成fd[fa]+fd[]

  下传时还要更新下面节点的g[]和f[],其中g[]由gd[fa]+f[]更新,f[]变成fd[fa]+f[]

  

  现在每个答案就比较好求了,因为代表的意义有当前右端点存在的影响,需要离线处理,将询问按右端点排序,然后枚举右端点,如果右端点与某个r重合,查询[l,r]内的最优g[]即可。

 #include<cstdio>
#include<cstdlib>
#include<algorithm> using namespace std; typedef long long ll;
const int nmax=,tmax=<<,shift=nmax; int n,m;
int M=,H=;
int a[nmax+],last[nmax*+],pre[nmax+];
ll f[tmax+],g[tmax+],fd[tmax+],gd[tmax+],ans[nmax+];
int l[nmax+],r[nmax+],p[nmax+]; bool cmp(int i,int j){
return r[i]<r[j];
} inline int max(int a,int b) {return a>b?a:b;} inline void update(ll &a,ll b){if(a<b) a=b;} inline void take(int i){
f[i]=max(f[i<<],f[i<<|]),g[i]=max(g[i<<],g[i<<|]);
} inline void give(int i,int j){
//fd[i]和gd[i]在于之前添加时可能没有顾及到下面的节点,于是偷懒加上tag表示这里可以下传的数目;
//其中gd[i]表示的是历史上给i的子树能添加的最大值,fd[i]表示总计能给它们添加多少
update(g[i],f[i]+gd[j]),f[i]+=fd[j];
update(gd[i],fd[i]+gd[j]),fd[i]+=fd[j];
} void pd(int i){
for(int h=H-,j;h;--h)//枚举i的所有祖先将它们的 tag下传
if(fd[j=i>>h] || gd[j])
give(j<<,j),give(j<<|,j),fd[j]=gd[j]=;
} void add(int l,int r,int x){
//f[i]表示计算从i到当前枚举值的整段之和
//g[i]表示必须选择i为左端点的情况下,到当前枚举值间的最优值。
//若g[i]不为叶子节点,则g[i]表示以其代表区间内的任意元素作为左端点的最优值
//f[i]不为叶节点时等于示以其代表区间内的任意元素作为左端点且当前枚举点为右端点时的最优值
for(pd(l+=M-),pd(r+=M+)/*传递(L,R)之间的所有标记*/;l^r^;take(l>>=),take(r>>=)/*这里只能传到 L,R会合之下的两端*/){ //因为不能每次传到底,所以给包含区间加上tag标记
if(~l&)
update(g[l^],f[l^]+=x),update(gd[l^],fd[l^]+=x);
if(r&)
update(g[r^],f[r^]+=x),update(gd[r^],fd[r^]+=x);
}
for(int tmp1,tmp2;l>>=;){//所以这里需要往上带到根节点去
tmp1=f[l],tmp2=g[l];
take(l);
if(f[l]==tmp1 && g[l]==tmp2) break;
}
} ll getmax(int l,int r){
//每次从l,r中选任意一个节点作为左端点,右端点不固定时的最优值即为答案。
ll Ans=;
for(pd(l+=M-),pd(r+=M+);l^r^;l>>=,r>>=){
if(~l&) update(Ans,g[l^]);
if(r&) update(Ans,g[r^]);
}
return Ans;
} int main(){
freopen("hungry.in","r",stdin);
freopen("hungry.out","w",stdout);
scanf("%d", &n);
while(n>=M-) M<<=,++H;
for(int i=;i<=n;i++)
scanf("%d",a+i),pre[i]=last[a[i]+shift],last[a[i]+shift]=i;//pre[i]表示上一个和它相同数所在的位置
scanf("%d",&m);
for(int i=;i<=m;i++)
scanf("%d%d",l+i,r+i),p[i]=i;
sort(p+,p+m+,cmp);//给右区间排序,因为g[x]表示的是从x开始,必须选x到当前枚举值之间的最优值,而当前枚举值相当于区间的右端点 for(int i=,j=;i<=n && j<=m;i++)
for(add(pre[i]+,i,a[i])/*往两个相同的数之间加,这样可以使重复的数只加一次*/;j<=m && r[p[j]]<=i;j++)
ans[p[j]]=getmax(l[p[j]],r[p[j]]); for(int i=;i<=m;i++)
printf("%I64d\n",ans[i]);
return ;
}

  Orz 人类智慧的伟大...+数据结构的巧妙结合...

Noip模拟考第三题——饥饿游戏的更多相关文章

  1. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  2. noip模拟23[联·赛·题]

    \(noip模拟23\;solutions\) 怎么说呢??这个考试考得是非常的惨烈,一共拿了70分,为啥呢 因为我第一题和第三题爆零了,然后第二题拿到了70分,还是贪心的分数 第一题和第二题我调了好 ...

  3. noip模拟35[第一次4题·裂了]

    noip模拟35 solutions 这是我第一次这么正式的考四个题,因为这四个题都出自同一个出题人,并不是拼盘拼出来的. 但是考得非常的不好,因为题非常难而且一直想睡觉.. 有好多我根本就不会的算法 ...

  4. noip模拟4[随·单·题·大佬]

    woc    woc   woc难斩了人都傻了 害上来先看T1,发现这不就是一个小期望嘛(有啥的)真是!!打算半个小时秒掉 可是吧,读着读着题面,发现这题面有大问题,后来去找老师,还是我nb给题挑错, ...

  5. NOIP 模拟 $13\; \text{玄学题}$

    题解 题如其名,是挺玄学的. 我们发现每个值是 \(-1\) 还是 \(1\) 只与它的次数是奇是偶有关,而 \(\sum_j^{j\le m}d(i×j)\) 又只与其中有多少个奇数有关 对于 \( ...

  6. NOIP 模拟 $13\; \text{工业题}$

    题解 本题不用什么推式子,找规律(而且也找不出来) 可以将整个式子看成一个 \(n×m\) 矩阵 考虑 \(f_{i,j}\),它向右走一步给出 \(f_{i,j}×a\) 的贡献,向下走一步给出 \ ...

  7. NOIP模拟13「工业题·卡常题·玄学题」

    T1:工业题 基本思路   这题有一个重要的小转化: 我们将原来的函数看作一个矩阵,\(f(i,j-1)*a\)相当于从\(j-1\)向右走一步并贡献a,\(f(i-1,j)*b\)相当于从\(i-1 ...

  8. Newnode's NOI(P?)模拟赛 第三题 (主席树优化建图 + tarjan)

    题目/题解戳这里 这道题题目保证a,b,ca,b,ca,b,c各是一个排列-mdzz考场上想到正解但是没看到是排列,相等的情况想了半天-然后写了暴力60分走人- 由于两两间关系一定,那么就是一个竞赛图 ...

  9. NOIP模拟 29

    T1第一眼觉得是网络流 看见4e6条边200次增广我犹豫了 O(n)都过不去的赶脚.. 可是除了网络流板子我还会什么呢 于是交了个智障的EK 还是用dijkstra跑的 居然有50分!$(RP--)$ ...

随机推荐

  1. HBase数据导出到HDFS

    一.目的 把hbase中某张表的数据导出到hdfs上一份. 实现方式这里介绍两种:一种是自己写mr程序来完成,一种是使用hbase提供的类来完成. 二.自定义mr程序将hbase数据导出到hdfs上 ...

  2. HOOK API 在多线程时应该注意的问题点

    在使用INLINE HOOK API实现对系统API的拦截时,正常情况下并没有太大问题,但一旦涉及到多线程,不管是修改IAT还是JMP,2种方法均会出现不可预料的问题,特别是在HOOK一些复杂的大型系 ...

  3. 对"使用Mono Runtime Bundle制作安装包让C#桌面应用程序脱离net framework"增加说明

    http://www.cnblogs.com/basilwang/archive/2011/11/29/2267809.html 想做独立引用的估计都看过这一篇文章,但是因为软件更新,很多地方已经不适 ...

  4. python Django 学习笔记(一)—— Django安装

    注:本人python版本2.7.5 ,win7系统 安装Django https://www.djangoproject.com/download/ 官方下载Django-1.5.5.tar.gz 1 ...

  5. xcode plugin

    http://alcatraz.io/ https://github.com/macoscope/CodePilot prepo  curl -fsSL https://raw.githubuserc ...

  6. spring aop 使用xml方式的简单总结

    spring aop的 xml的配置方式的简单实现: 1.编写自己的切面类:配置各个通知类型 /** * */ package com.lilin.maven.service.aop; import ...

  7. Lex+YACC详解

    1. 简介 只要你在Unix环境中写过程序,你必定会邂逅神秘的Lex&YACC,就如GNU/Linux用户所熟知的Flex&Bison,这里的Flex就是由Vern Paxon实现的一 ...

  8. NE、EQ等比较操作符的意义

    比较所有的 字段类型 要比较所有 的字段类型 ,可以在逻 辑表达式中 使用下列运 算符: <运算符>含 义 EQ 等于 = 等于 NE 不 等于 <> 不 等于 >< ...

  9. ExtJs4学习MVC中的Store

    Ext.data.Store是extjs中用来进行数据交换和数据交互的标准中间件,无论是Grid还是ComboBox,都是通过它实现数据读取.类型转换.排序分页和搜索等操作的. 1 2 3 4 5 6 ...

  10. C#中的委托与事件

    1,委托? 通俗来讲,就是一个能存放符合某种格式(签名)的方法的指针 的容器  (可以将方法作为一个参数来传递到另一个方法内执行) 定义委托:delegate string DelegateSayHi ...