【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; }