还教室
题目描述 Description###
在接受借教室请求的 \(n\) 天中,第 \(i\) 天剩余的教室为 \(a_i\) 个。作为大学借教室服务的负责人,你需要完成如下三种操作共 \(m\) 次:
① 第 \(l\) 天到第 \(r\) 天,每天被归还 \(d\) 个教室。
② 询问第 \(l\) 天到第 \(r\) 天教室个数的平均数。
③ 询问第 \(l\) 天到第 \(r\) 天教室个数的方差。
输入描述 Input Description###
第一行包括两个正整数 \(n\) 和 \(m\) ,其中 \(n\) 为借教室的天数,\(m\) 为操作次数。
接下来一行, 共包含 \(n\) 个整数, 第 \(i\) 个整数表示第 \(i\) 天剩余教室数目为 \(a_i\) 个。
接下来 \(m\) 行,每行的第一个整数为操作编号(只能为 \(1\) 或 \(2\) 或 \(3\)) ,接下来
包含两个正整数 \(l\) 和 \(r\),若操作编号为 \(1\),则接下来再包含一个正整数 \(d\) 。
输出描述 Output Description###
对于每个操作 \(2\) 和操作 \(3\) ,输出一个既约分数(分子与分母互质)表示询问的答案(详见样例) 。若答案为 \(0\) ,请输出“0/1” (不含引号) 。
样例输入 Sample Input###
5 4
1 2 3 4 5
1 1 2 3
2 2 4
3 2 4
3 1 5
样例输出 Sample Output###
4/1
2/3
14/25
数据范围及提示 Data Size & Hint###
\(1<=n,m<=100000\)
之前的一些废话###
听说已经出期中考试成绩了,不管了不管了!
题解###
练习线段树的,维护一下区间和与区间平方和即可。
代码###
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<LL,LL> PII;
#define X first
#define Y second
#define mp make_pair
inline 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;
}
const int maxn=100010;
int n,m,tp,x,y,z,a[maxn];
LL sum[maxn<<2],qsum[maxn<<2],tag[maxn<<2];
void pushup(int l,int r,int o)
{
if(l==r)return;
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
sum[o]=sum[lo]+sum[ro];
qsum[o]=qsum[lo]+qsum[ro];
return;
}
void pushdown(int l,int r,int o)
{
if(l==r)return;
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
tag[lo]+=tag[o];tag[ro]+=tag[o];
qsum[lo]+=(2ll*tag[o]*sum[lo]+(LL)(mid-l+1)*tag[o]*tag[o]);
qsum[ro]+=(2ll*tag[o]*sum[ro]+(LL)(r-mid)*tag[o]*tag[o]);
sum[lo]+=tag[o]*(LL)(mid-l+1);sum[ro]+=tag[o]*(LL)(r-mid);
tag[o]=0;
}
void build(int l,int r,int o)
{
if(l==r)
{
sum[o]=a[l];
qsum[o]=(LL)a[l]*(LL)a[l];
return;
}
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
build(l,mid,lo);build(mid+1,r,ro);
pushup(l,r,o);
}
void change(int l,int r,int o,int ql,int qr,LL x)
{
if(l==ql && r==qr)
{
tag[o]+=x;
qsum[o]+=(2ll*x*sum[o]+(LL)(r-l+1)*x*x);
sum[o]+=(LL)(r-l+1)*x;
return;
}
pushdown(l,r,o);
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
if(qr<=mid)change(l,mid,lo,ql,qr,x);
else if(ql>mid)change(mid+1,r,ro,ql,qr,x);
else change(l,mid,lo,ql,mid,x),change(mid+1,r,ro,mid+1,qr,x);
pushup(l,r,o);
}
LL query1(int l,int r,int o,int ql,int qr)
{
if(l==ql && r==qr)return sum[o];
pushdown(l,r,o);
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
if(qr<=mid)return query1(l,mid,lo,ql,qr);
else if(ql>mid)return query1(mid+1,r,ro,ql,qr);
else return query1(l,mid,lo,ql,mid)+query1(mid+1,r,ro,mid+1,qr);
}
LL query2(int l,int r,int o,int ql,int qr)
{
if(l==ql && r==qr)return qsum[o];
pushdown(l,r,o);
int mid=(l+r)>>1,lo=o<<1,ro=lo|1;
if(qr<=mid)return query2(l,mid,lo,ql,qr);
else if(ql>mid)return query2(mid+1,r,ro,ql,qr);
else return query2(l,mid,lo,ql,mid)+query2(mid+1,r,ro,mid+1,qr);
}
LL gcd(LL a,LL b){return b==0ll ? a : gcd(b,a%b);}
PII yuefen(PII a)
{
LL c=gcd(a.X,a.Y);
return mp(a.X/c,a.Y/c);
}
void print(PII a){a=yuefen(a);printf("%lld/%lld\n",a.X,a.Y);}
int main()
{
freopen("rand.in","r",stdin);
//freopen("myans.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,n,1);
while(m--)
{
tp=read();x=read();y=read();
if(tp==1)z=read(),change(1,n,1,x,y,(LL)z);
else if(tp==2)print(mp(query1(1,n,1,x,y),(LL)(y-x+1)));
else if(tp==3)
{
LL sum1=query1(1,n,1,x,y),sum2=query2(1,n,1,x,y),len=y-x+1ll;
print(mp((len)*sum2-sum1*sum1,len*len));
}
}
return 0;
}
总结###
相关线段树易错点已整理到我的笔记本中。