【问题描述】
还记得NOIP 2012提高组Day2中的借教室吗?时光飞逝,光阴荏苒,两年
过去了,曾经借教室的同学们纷纷归还自己当初租借的教室。请你来解决类似于
借教室的另一个问题。
在接受借教室请求的n天中,第i天剩余的教室为ai个。作为大学借教室服
务的负责人,你需要完成如下三种操作共m次:
① 第l天到第r天,每天被归还d个教室。
② 询问第l天到第r天教室个数的平均数。
③ 询问第l天到第r天教室个数的方差。
【输入格式】
第一行包括两个正整数n和m,其中n为借教室的天数,m 为操作次数。
接下来一行,共包含n个整数,第i个整数表示第i天剩余教室数目为ai个。
接下来m行,每行的第一个整数为操作编号(只能为1或2或3),接下来
包含两个正整数l和r,若操作编号为1,则接下来再包含一个正整数d。
【输出格式】
对于每个操作2和操作3,输出一个既约分数(分子与分母互质)表示询问
的答案(详见样例)。若答案为0,请输出“0/1”(不含引号)。
【样例输入】
5 4
1 2 3 4 5
1 1 2 3
2 2 4
3 2 4
3 1 5
【样例输出】
4/1
2/3
14/25
【数据范围】
对于全部测试数据满足:1 ≤ l ≤ r ≤ n,0 ≤ ai ≤ 10,1 ≤ d ≤ 3,操作 1 的数量
不超过 10%。注意:ai和 d 的范围很小及操作 1 数量很少的原因是为了保证答案
的分子不会很大,以防止答案的分子溢出 64 位整数的范围,这与题目做法无关。
【题解】
一看这种描述如此套路的题目就知道是数据结构题。
相当于维护区间加法,区间平均值,区间方差,这个可以用线段树维护。
平均值可以由区间和除以区间长度维护
区间方差,即∑(x ̅-xi)2**,可以转化为**(∑(x2) *len-(∑x)2)/(len2) 其中len是区间的长度
所以,只需要维护∑(x^2)和∑(x),也就是区间和以及区间平方和,就可以了。
剩下的就是线段树的事情了。
(调了2个半小时,还写了对拍,最后发现有一个地方忘long long了......)
【代码】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<set>
using namespace std;
#define f(i,n) for(int i=1;i<=(n);i++)
#define ll long long
#define INF 1<<30
#define N 100010
int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while( isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int a[N];
ll maxn[3*N],sum[3*N],minn[3*N],sumf[3*N],tag[3*N];
int n,q;
int z,ql,qr;
ll addi;
void pushup(int o,int l,int r)
{
int lo=2*o,ro=2*o+1,m=(l+r)>>1;
sum[o]=sum[lo]+sum[ro];
maxn[o]=max(maxn[lo],maxn[ro]);
minn[o]=min(minn[lo],minn[ro]);
sumf[o]=sumf[lo]+sumf[ro];
}
void dingyi(int o,int l,int r)
{
if(l==r)
{
sum[o]=maxn[o]=minn[o]=a[l];
sumf[o]=a[l]*a[l];
return;
}
int lo=2*o,ro=2*o+1,m=(l+r)>>1;
dingyi(lo,l,m);
dingyi(ro,m+1,r);
pushup(o,l,r);
}
void pushdown(int o,int l,int r)
{
if(l==r)return;
int lo=2*o,ro=2*o+1,m=(l+r)>>1;
if(tag[o])
{
tag[lo]+=tag[o];
tag[ro]+=tag[o];
sumf[lo]+=(ll)(m-l+1)*tag[o]*tag[o]+(ll)2*tag[o]*sum[lo];
sumf[ro]+=(ll)(r-m)*tag[o]*tag[o]+(ll)2*tag[o]*sum[ro];
sum[lo]+=(ll)(m-l+1)*tag[o];
sum[ro]+=(ll)(r-m)*tag[o];
maxn[lo]+=tag[o];
maxn[ro]+=tag[o];
minn[lo]+=tag[o];
minn[ro]+=tag[o];
}
tag[o]=0;
}
void add(int o,int l,int r)
{
if(l>=ql&&r<=qr)
{
sumf[o]+=(ll)(r-l+1)*addi*addi+2ll*addi*sum[o];
sum[o]+=(ll)(r-l+1)*addi;
maxn[o]+=addi;
minn[o]+=addi;
tag[o]+=addi;
return;
}
pushdown(o,l,r);
int lo=2*o,ro=2*o+1,m=(l+r)>>1;
if(ql<=m)add(lo,l,m);
if(qr>m)add(ro,m+1,r);
pushup(o,l,r);
}
ll qsum=0,qsumf=0;
void query(int o,int l,int r)
{
if(l>=ql&&r<=qr)
{
qsum+=sum[o];
qsumf+=sumf[o];
return;
}
int lo=2*o,ro=2*o+1,m=(l+r)>>1;
pushdown(o,l,r);
if(ql<=m)query(lo,l,m);
if(qr>m)query(ro,m+1,r);
pushup(o,l,r);
}
ll xj(ll a,ll b)
{
if(b==0)return a;
return xj(b,a%b);
}
void qj(ll a,ll b)
{
ll s=xj(a,b);
printf("%lld/%lld
",a/s,b/s);
}
int main()
{
n=read();
q=read();
f(i,n)a[i]=read();
dingyi(1,1,n);
f(i,q)
{
z=read();
ql=read();
qr=read();
if(z==1)
{
scanf("%lld",&addi);
if(addi==0)continue;
add(1,1,n);
}
if(z==2)
{
qsum=0;
qsumf=0;
query(1,1,n);
qj(qsum,qr-ql+1);
}
if(z==3)
{
qsum=0;
qsumf=0;
query(1,1,n);
//cout<<"qsum "<<qsum<<" qsumf "<<qsumf<<endl;
qj(qsumf*(ll)(qr-ql+1)-qsum*qsum,(ll)(qr-ql+1)*(ll)(qr-ql+1));
}
}
}