【HAOI2012】高速公路

题面

https://www.luogu.org/problem/P2221

题解

其实我认为这道题还挺难的。。。。虽然是道水题罢了。

这道题让我自闭了两次,可见我现在学习状态之差,整个题从思考到写出来竟然花了下午的一个小时和一个晚上的时间。

马上的$NOIP$,如果还是以这种状态,肯定是考不好的。所以这一个月一定要大刀阔斧的把自己心中的浮躁之火灭下去。

首先,一看到这个形式我就想到考虑贡献,其实这是$fake$的,应该是一个线段树维护分治信息。

分治维护的信息有哪些呢?

  • 答案肯定要。
  • 合并两个区间的时候还需要算一个阶梯一样的前缀和,总和肯定也要
  • 区间的$siz$

下列是注意事项:

  • 一开始算错答案的最大值,以为要开$\_\_int128$,事实上,答案的最大值为$$10000 \sum_{i=1}^{n}{i(n-i)}=10000(\frac{n(n+1)^{2}}{2}-\frac{n(n+1)(2n+1)}{6}).$$(什么,你问证明,公式在合肥八中楼梯上写着呢,证明$syj$都只会用归纳法我怎么可能知道,翻《具体数学》去吧),是$10$的$19$次方级别的数,如果算上一个$\frac{1}{6}$的常数,大概是能够的。。。。。
  • $node$结构体,表示一个区间的分治信息,我们需要一个合并的函数,在合并的函数中,不仅要维护$sl,sr,s,ans$,还要维护$siz$和$tag$(和答案关系不大,容易忘啊),事实上,$tag$不应该出现在上面,它应该出现在线段树上面,这样是更简练的,但我没有改。
  • 瞪大狗眼好好看看,贡献是加的,我算的时候想当然,以我以前水题的经验,不加思考的乘了起来。。。。。。
  • 事实上,$sl$和$sr$的含义也是容易弄混淆的,要看怎么定义的,我们最后需要的是一个二阶前缀和,阶梯的话可能弄反了。。
  • 函数返回值为自定义结构体的时候记得把$ret$$return$回去,不然的话会出现奇怪的很大的值。
  • 对于$merge$函数不太好定义一个单位$node$,那线段树就换一种写法,在向下递归的时候就判断,不交不向下。
  • 老生常谈的东西了,向下递归之前$pushdown$,$pushdown$的时候记得下传$tag$($pushdown$操作可以把区间修改完全覆盖的代码复制过去,然后把$x$改称$ls$和$rs$)。
  • 算等差数列前$n$项和,记得除二($syj$:揍死你)
  • 乘的项数比较多,保险的做法是每乘一个就在中间乘一个$1LL$
#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
#define N 100050
#define LL long long
using namespace std;

int n,m;
struct node {
  int siz;
  LL sl,sr,s;
  LL ans;
  int ts;
} t[N<<2];

#define ls (x<<1)
#define rs (x<<1|1)

void maketree(int x,int lb,int rb) {
  t[x].siz=rb-lb+1;
  t[x].sl=t[x].sr=t[x].s=t[x].ans=0;
  t[x].ts=0;
  if (lb==rb) return;
  int mid=(lb+rb)>>1;
  maketree(ls,lb,mid); maketree(rs,mid+1,rb);
}

node merge(node a,node b) {
  node ret;
  ret.ans=a.sr*b.siz+b.sl*a.siz+a.ans+b.ans;
  ret.s=a.s+b.s;
  ret.sl=a.sl+b.siz*a.s+b.sl;
  ret.sr=b.sr+a.siz*b.s+a.sr;
  ret.siz=a.siz+b.siz;
  ret.ts=0;
  return ret;
}

void pushdown(int x) {
  int k=t[x].ts;
  t[x].ts=0;
  t[ls].sl+=(LL)((1+t[ls].siz)*1LL*t[ls].siz)/2*1LL*k;
  t[ls].sr+=(LL)((1+t[ls].siz)*1LL*t[ls].siz)/2*1LL*k;
  t[ls].s+=(LL)(t[ls].siz)*1LL*k;
  t[ls].ans+=((LL)(t[ls].siz+1)*1LL*(t[ls].siz+1)*1LL*t[ls].siz/2-(LL)(t[ls].siz*1LL*(t[ls].siz+1)*1LL*(2*t[ls].siz+1))/6)*1LL*k;
  t[ls].ts+=k;

  t[rs].sl+=(LL)((1+t[rs].siz)*1LL*t[rs].siz)/2*k;
  t[rs].sr+=(LL)((1+t[rs].siz)*1LL*t[rs].siz)/2*k;
  t[rs].s+=(LL)(t[rs].siz)*1LL*k;
  t[rs].ans+=((LL)(t[rs].siz+1)*1LL*(t[rs].siz+1)*1LL*t[rs].siz/2-(LL)(t[rs].siz*1LL*(t[rs].siz+1)*1LL*(2*t[rs].siz+1))/6)*1LL*k;
  t[rs].ts+=k;
}

void modify(int x,int lb,int rb,int l,int r,int v) {
  if (l<=lb && rb<=r) {
    t[x].sl+=(LL)((1+t[x].siz)*1LL*t[x].siz)/2*1LL*v;
    t[x].sr+=(LL)((1+t[x].siz)*1LL*t[x].siz)/2*1LL*v;
    t[x].s+=(LL)(t[x].siz)*1LL*v;
    t[x].ans+=((LL)(t[x].siz+1)*1LL*(t[x].siz+1)*1LL*t[x].siz/2-(LL)(t[x].siz*1LL*(t[x].siz+1)*1LL*(2*t[x].siz+1))/6)*1LL*v;
    t[x].ts+=v;
    return;
  }
  if (lb>r || rb<l) {
    return;
  }
  pushdown(x);
  int mid=(lb+rb)>>1;
  modify(ls,lb,mid,l,r,v); modify(rs,mid+1,rb,l,r,v);
  t[x]=merge(t[ls],t[rs]);
}

node query(int x,int lb,int rb,int l,int r) {
  if (l<=lb && rb<=r) {
    return t[x];
  }
  pushdown(x);
  int mid=(lb+rb)>>1;
  if (r<=mid) return query(ls,lb,mid,l,r);
  if (l>mid) return query(rs,mid+1,rb,l,r);
  return merge(query(ls,lb,mid,l,r),query(rs,mid+1,rb,l,r));
}

LL gcd(LL a,LL b) {
  if (b==0) return a;
  return gcd(b,a%b);
}

int main() {
  char opt[5];
  scanf("%d %d",&n,&m); n--;
  maketree(1,1,n);
  for (ri i=1,l,r,v;i<=m;i++) {
    scanf("%s",opt);
    if (opt[0]=='Q') {
      scanf("%d %d",&l,&r); r--;
      node ret=query(1,1,n,l,r);
      LL tms=(r-l+1)*(LL)(r-l)/2+r-l+1;
      LL d=gcd(tms,ret.ans%tms);
      long long a=(LL)(ret.ans/d),b=(LL)(tms/d);
      printf("%lld/%lld\n",a,b);
    }
    else {
      scanf("%d %d %d",&l,&r,&v); r--;
      modify(1,1,n,l,r,v);
    }
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/shxnb666/p/11657941.html