Solution
考虑每一个询问答案的分子
用线段树维护序列,每个节点记录以下内容
- 本区间的长度 (len)
- 本区间的边权和 (sum)
- 自左向右的二阶前缀和 (vl=sum_{i} ia_i)
- 自右向左的二阶前缀和 (vr=sum_i (len-i)a_i)
- 本区间内的答案 (ans)
- 本区间的加法标记 (tag)
这样就可以对区间进行 (O(1)) 的快速合并
考虑如何下传标记,设要放置的标记值为 (t),则
- (sum+tlen o sum)
- (vl+frac{len(len+1)}{2}t o vl, vr+frac{len(len+1)}{2}t o vr)
- (ans + p_{len}t o ans),其中 (p_{i}=sum_{j=1}^i (len-i+1)i),有递推关系 (p_i=p_{i-1}+frac{i(i-1)}{2}+i),故可以预处理出
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int P[N],a[N];
struct node {
int len,sum,vl,vr,ans,tag;
void print() {
cout<<len<<" "<<sum<<" "<<vl<<" "<<vr<<" "<<ans<<" "<<tag<<endl;
}
} tr[N];
node merge(node l,node r) {
node ret;
ret.len = l.len+r.len;
ret.sum = l.sum+r.sum;
ret.vl = l.vl+r.vl+l.len*r.sum;
ret.vr = r.vr+l.vr+r.len*l.sum;
ret.ans = l.ans+r.ans+l.vl*(r.len)+r.vr*(l.len);
ret.tag = 0;
return ret;
}
void pushdown(int p) {
if(tr[p].tag) {
int t=tr[p].tag;
tr[p*2].sum+=t*tr[p*2].len;
tr[p*2].vl+=tr[p*2].len*(tr[p*2].len+1)/2*t;
tr[p*2].vr+=tr[p*2].len*(tr[p*2].len+1)/2*t;
tr[p*2].ans+=P[tr[p*2].len]*t;
tr[p*2+1].sum+=t*tr[p*2+1].len;
tr[p*2+1].vl+=tr[p*2+1].len*(tr[p*2+1].len+1)/2*t;
tr[p*2+1].vr+=tr[p*2+1].len*(tr[p*2+1].len+1)/2*t;
tr[p*2+1].ans+=P[tr[p*2+1].len]*t;
tr[p*2].tag+=t;
tr[p*2+1].tag+=t;
tr[p].tag=0;
}
}
void build(int p,int l,int r) {
if(l==r) {
tr[p]=node({1,a[l],a[l],a[l],a[l],0});
}
else {
build(p*2,l,(l+r)/2);
build(p*2+1,(l+r)/2+1,r);
tr[p]=merge(tr[p*2],tr[p*2+1]);
}
}
void modify(int p,int l,int r,int ql,int qr,int t) {
if(l>qr || r<ql) return;
if(l>=ql&&r<=qr) {
tr[p].sum+=t*tr[p].len;
tr[p].vl+=tr[p].len*(tr[p].len+1)/2*t;
tr[p].vr+=tr[p].len*(tr[p].len+1)/2*t;
tr[p].ans+=P[tr[p].len]*t;
tr[p].tag+=t;
}
else {
pushdown(p);
modify(p*2,l,(l+r)/2,ql,qr,t);
modify(p*2+1,(l+r)/2+1,r,ql,qr,t);
tr[p]=merge(tr[p*2],tr[p*2+1]);
}
}
node query(int p,int l,int r,int ql,int qr) {
if(l>qr || r<ql) return node({0,0,0,0,0,0});
if(l>=ql&&r<=qr) return tr[p];
pushdown(p);
return merge(query(p*2,l,(l+r)/2,ql,qr),query(p*2+1,(l+r)/2+1,r,ql,qr));
}
int n,m,t1,t2,t3;
char op;
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m;
--n;
P[1]=1;
for(int i=2;i<=n;i++) P[i]=P[i-1]+i*(i-1)/2+i;
build(1,1,n);
while(m--) {
cin>>op;
if(op=='C') {
cin>>t1>>t2>>t3;
--t2;
modify(1,1,n,t1,t2,t3);
}
else {
cin>>t1>>t2;
--t2;
int ans = query(1,1,n,t1,t2).ans;
int mom = (t2-t1+1)*(t2-t1+2)/2;
int g = __gcd(ans,mom);
ans/=g; mom/=g;
cout<<ans<<"/"<<mom<<endl;
}
//for(int i=1;i<=10;i++) tr[i].print();
}
}