借教室续

Description

在接受借教室请求的n 天中,第i 天剩余的教室为ai个。作为大学借教室服务的负责人,你需要完成如下三种操作共m次:
① 第 l 天到第r 天,每天被归还d 个教室。
② 询问第l 天到第r 天教室个数的平均数。
③ 询问第l 天到第r 天教室个数的方差。

Input

第一行包括两个正整数n 和m,其中n 为借教室的天数,m为操作次数。
接下来一行,共包含n个整数,第i 个整数表示第i天剩余教室数目为ai个。
接下来m 行,每行的第一个整数为操作编号(只能为1 或2 或3),接下来包含两个正整数l 和r,若操作编号为1,则接下来再包含一个正整数d。

Output

对于每个操作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

HINT

1 ≤ n, m ≤ 100,000 ,1 ≤ l ≤ r ≤ n,0 ≤ ai ≤ 10,1 ≤ d ≤ 3,操作1 的数量不超过10%。

Key To Problem

这是一道线段树的题
首先方差绝对不可以直接来求,而是要用一个推导的公式

s2=(Σni=1x2in)((Σni=1xi)2n2)

我们需要两个线段树,数组f记录区间和,数组sum记录区间平方和
修改前

sumrt=f2l+f2l+1+......+f2r
frt=fl+fl+1+......+fr

修改后

sumrt=fl+d2+(fl+1+d)2+......+(fr+d)2
sumrt=(f2l+f2l+1+......+f2r)+2d(fl+fl+1+......+fr)+d2(rl+1)
sumrt=sumrt+2dfrt+d2(rl+1)

剩下的就是普通的区间修改加上求和之类的啦

CODE

#include<cstdio>
#include<algorithm>
#define N 100010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
typedef long long ll;
int n,m;
ll f[N<<2];
ll sum[N<<2];
ll add[N<<2];

ll gcd(ll x,ll y)
{
    if(y==0)
        return x;
    ll t=gcd(y,x%y);
    return t;
}

void PushUp(int rt)
{
    f[rt]=f[ls]+f[rs];
    sum[rt]=sum[ls]+sum[rs];
}

void PushDown(int rt,ll k)
{
    if(add[rt])
    {
        add[ls]+=add[rt];
        add[rs]+=add[rt];
        sum[ls]+=2*add[rt]*f[ls]+(k-(k>>1))*add[rt]*add[rt];
        sum[rs]+=2*add[rt]*f[rs]+(k>>1)*add[rt]*add[rt];
        f[ls]+=add[rt]*(k-(k>>1));
        f[rs]+=add[rt]*(k>>1);
        add[rt]=0;
    }
}

void build(int l,int r,int rt)
{
    add[rt]=0;
    if(l==r)
    {
        scanf("%d",&f[rt]);
        sum[rt]=f[rt]*f[rt];
        return ;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}

void update(int L,int R,ll s,int l,int r,int rt)
{
    if(l>=L&&r<=R)
    {
        add[rt]+=s;
        sum[rt]+=2*s*f[rt]+(r-l+1)*s*s;
        f[rt]+=s*(ll)(r-l+1);
        return ;
    }
    PushDown(rt,(ll)r-l+1);
    int mid=(l+r)>>1;
    if(mid>=L)
        update(L,R,s,lson);
    if(mid<R)
        update(L,R,s,rson);
    PushUp(rt);
}

ll query1(int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R)
        return f[rt];
    PushDown(rt,r-l+1);
    int mid=(l+r)>>1;
    ll ret=0;
    if(mid>=L)
        ret+=query1(L,R,lson);
    if(mid<R)
        ret+=query1(L,R,rson);
    return ret;
}

ll query2(int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R)
        return sum[rt];
    PushDown(rt,r-l+1);
    int mid=(l+r)>>1;
    ll ret=0;
    if(mid>=L)
        ret+=query2(L,R,lson);
    if(mid<R)
        ret+=query2(L,R,rson);
    return ret;
}

int main()
{
    freopen("classroom.in","r",stdin);
    freopen("classroom.out","w",stdout);
    scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i=1;i<=m;i++)
    {
        int a,x,y;
        ll z;
        scanf("%d%d%d",&a,&x,&y);
        if(a==1)
        {
            scanf("%I64d",&z);
            update(x,y,z,1,n,1);
        }else
        {
            if(a==2)
            {
                ll ans=query1(x,y,1,n,1);
                ll s=y-x+1;
                ll k=gcd(ans,s);
                printf("%I64d/%I64d\n",ans/k,s/k);
            }else
            {
                ll s=y-x+1;
                ll p=query1(x,y,1,n,1);
                p*=p;
                ll ans=query2(x,y,1,n,1)*s-p;
                s*=s;
                ll k=gcd(ans,s);
                printf("%I64d/%I64d\n",ans/k,s/k);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值