现在的位置: 首页 > 综合 > 正文

小白逛公园

2013年10月04日 ⁄ 综合 ⁄ 共 3048字 ⁄ 字号 评论关闭
小白逛公园                             

题目描述

  小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

  一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。

  那么,就请你来帮小白选择公园吧。

输入格式

  第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。

  接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。

  接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。

  其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。

输出格式

  小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

样例输入

5 3

1

2

-3

4

5

1 2 3

2 2 -1

1 2 3

样例输出

2

-1

评测链接:rqnoj:http://www.rqnoj.cn/Problem_626.html

                    vijos:https://vijos.org/p/1083

(vijos上数据比较弱,用来检验一下你写对没就行了,最好是能够交到rqnoj上通过了,才说明你写的算是过关了。)

 解法:

这是鄙人第一次写的比较高级的线段树,用了包括内敛函数、手写读入输出优化等各种优化,交了n遍总算通过了。

 我交到rqnoj上一共是耗费了4813ms,第一的大牛是2281ms,水平不够,不晓得是哪么做的,有大牛知道点拨一下,万分感激。

好了,接下来说我自己的解题思路:

1.用d[p].l记录区间p从用左端开始的最大连续区间字段和,d[p].r记录区间p从用右端开始的最大连续区间字段和,d[p].s记录区间p的总和,d[p].s记录区间p的最大连续子区间和。

2.手写读入输出,内敛函数。

3.用dd[i]记录[i,i]在线段树中的位置,用build_init()直接读入最开始的一棵树,当l==r时就读入,dd[l]就赋值为此时的节点编号。

4.用f[j]记录线段树中节点j的子树是否有变化,每次重建树时,只对子树有变化的节点进行重建。

5.用q[maxn]记录一段连续的操作2中,被改变的节点编号,直到遇到第一个操作1时,就先对q队列中的节点的所有父亲结点的f值标记为1,然后进行树的重建。

有一些详细的代码细节就不再赘述,可以看我的代码(按上面的分点来看,应该能看懂)。

代码:

#include<Cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define maxn (500000+100)
using namespace std;

struct tnode{int l,r,s,m;}d[maxn*3]; 
int dd[maxn],q[maxn];
bool f[maxn*3];

void init()
{
  freopen("rq626.in","r",stdin);
  freopen("rq626_4.out","w",stdout);
}
//手写读入输出 
inline int getin()
{
  int ans=0;bool sign=0;char tmp;
  do tmp=getchar();
  while(!isdigit(tmp)&&tmp!='-');
  if(tmp=='-')tmp=getchar(),sign=1;
  do ans=(ans<<3)+(ans<<1)+tmp-'0';
  while(isdigit(tmp=getchar()));
  return sign?-ans:ans;
}
//得到区间a,b的父亲结点的各项值 
inline tnode data(tnode a,tnode b)
{
  tnode k;
  k.l=max(a.l,a.s+b.l);
  k.r=max(b.r,b.s+a.r);
  k.m=max(max(a.m,b.m),a.r+b.l);
  k.s=a.s+b.s;  
  return k;
}
//读入原始的第一棵树 
void build_init(int p,int l,int r)
{
  if(l==r)
    {
      int x=getin();dd[l]=p;
      d[p].l=d[p].r=d[p].m=d[p].s=x;
      return;
    }
  int m=(l+r)>>1,k=p<<1;
  build_init(k,l,m),build_init(k+1,m+1,r);
  d[p]=data(d[k],d[k+1]);  
}
//对子节点有变化的点进行染色 
inline void ranse(int i)
{
  int k=i>>1;
  while(k>1 && !f[k])
    f[k]=1,k=k>>1;
}
//对子节点有变化的树进行重建 
void build(int p,int l,int r)
{
  int m=(l+r)>>1,k=p<<1;
  if(f[k])build(k,l,m),f[k]=0;
  if(f[k+1])build(k+1,m+1,r),f[k+1]=0;
  d[p]=data(d[k],d[k+1]);
}

tnode get(int p,int pl,int pr,int l,int r)
{
  if(l<=pl && pr<=r)return d[p];
  int m=(pl+pr)>>1,k=p<<1;
  if(r<=m) return get(k,pl,m,l,r);//[l,r]只在p的左子树 
  if(l>m) return get(k+1,m+1,pr,l,r);//[l,r]只在p的右子树 
  tnode t1=get(k,pl,m,l,r);//跨区间 
  tnode t2=get(k+1,m+1,pr,l,r);
  return data(t1,t2);
}

void work()
{
  int n=getin(),m=getin();
  build_init(1,1,n); int i;
  for(i=1;i<=n;i++)ranse(dd[i]);
  
  //q2记录上一个操作是否是2
  //q[maxn]记录连续的一段操作2所改变的节点,遇到第一个操作1时就对子节点有
  //变化的点进行重建。 
  bool q2=0; int l,r=0,kk,k,a,b;
  for(i=1;i<=m;i++)
    {
      k=getin();a=getin();b=getin();
      if(k==2)
        {
          kk=dd[a],q2=1; q[++r]=kk;
          d[kk].l=d[kk].r=d[kk].m=d[kk].s=b;
        }
      else
        {
          if(a>b)swap(a,b); l=1;
          while(l<=r)ranse(q[l++]);
          r=0;
          if(n>1 && q2)build(1,1,n);//判断n>1,若n=1就不必建树了;
          q2=0;                    //q2=1,代表这是一段连续操作2后的第一个操作1  
          printf("%d\n",get(1,1,n,a,b).m);
        }
    }
}

int main()
{
  init();
  work();
  return 0;
}

抱歉!评论已关闭.