HAOI2012 高速公路 线段树

题目描述

Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。 Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。 政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。 无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l<r),在第l个到第r个收费站里等概率随机取出两个不同的收费站a和b,那么从a行驶到b将期望花费多少费用呢?

输入格式

第一行2个正整数N,M,表示有N个收费站,M次调整或询问 接下来M行,每行将出现以下两种形式中的一种 C l r v 表示将第l个收费站到第r个收费站之间的所有道路的通行费全部增加v Q l r 表示对于给定的l,r,要求回答小A的问题 所有C与Q操作中保证1<=l<r<=N

输出格式

对于每次询问操作回答一行,输出一个既约分数 若答案为整数a,输出a/1

样例
样例输入
4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4
样例输出
1/1
8/3
17/6
数据范围与提示

所有C操作中的v的绝对值不超过10000

在任何时刻任意道路的费用均为不超过10000的非负整数

所有测试点的详细情况如下表所示

Test N M

1 =10 =10

2 =100 =100

3 =1000 =1000

4 =10000 =10000

5 =50000 =50000

6 =60000 =60000

7 =70000 =70000

8 =80000 =80000

9 =90000 =90000

10 =100000 =100000

  首先这个题一看就是一道数据结构题,废话,考虑一下这个题要求什么,一般用分数表示的概率期望都是假的,不要问我为什么。。这道题其实就是要找一个子区间的和,再除以选端点的方案数$C_{len}^{2}$,那么好像暴力只能用$O(n^3)$打,毕竟枚举子区间就要$n^2$的。

  我们现在要考虑暴力如何优化,这道题和HAOI的另一道题树上染色的思路很像,那个题中两点距离通过找边的贡献得到,进而把枚举两点变做加边贡献,那么这个题也从这个思路考虑,枚举每一段路的贡献,那么我们就的到了$O(n)$修改/查询的式子:

$\sum \limits_{i=l}^{r} s[i]*(r-i+1)*(i-l+1)$

这个使我们成功转化思路,这样的优化暴力能水过90分,已经非常优秀了。如果要A过,我们考虑用线段树去维护这个东西,我们首先的对这个式子变形,目的是得到可以区间合并的并且log查询的东西。我们先把括号都拆掉。

  $\sum \limits_{i=l}^{r} s[i]*i*(l+r)-s[i]*i^{2}-s[i]*(l-1)*(r+1)$

  然后把常数提出来,能一起维护的放在一起。

  $(l+r)*\sum \limits_{i=l}^{r} s[i]*i-\sum \limits_{i=l}^{r}s[i]*i^{2}-(l-1)(r+1)*\sum \limits_{i=l}^{r}s[i]$

  那么我们已经发现,此时我们对于一个区间,只需要维护它的$s[i]$,$s[i]*i^{2}$,$s[i]*i$就可以了。

  查询已经解决,那么如何修改呢,$s[i]$,$s[i]*i$都比较简单,一个是加$w*1$,一个是$s[i]*i+w*i$等差数列即可,最后是$s[i]*i^{2}+w*i^{2}$,这个平方就要用到自然数方幂和公式,借此机会习得拉格朗日插值。当然只要知道公式就可以了$n(n+1)(2n+1)/6$。

  友情提示,懒标记是用来叠加的,不是用来覆盖的。。。。

  

#include<iostream>
#include<cstdio>
#define ll long long
#define int long long
using namespace std;
const int N=1000020,M=100020;
struct node
{
    ll s,si,sii;
    node operator +(node b)
    {
        node c;c.s=c.si=c.sii=0;
        c.s=s+b.s;
        c.si=si+b.si;
        c.sii=sii+b.sii;
        return c;
    }
};
struct tree{int l,r;ll f;node s;}tr[4*N];
int rd()
{
    int s=0,w=1;
    char cc=getchar();
    while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();}
    while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
    return s*w;
}
void change(int k,int w)
{
    int l=tr[k].l,r=tr[k].r;
    tr[k].s.s+=1ll*w*(r-l+1);
    tr[k].s.si+=1ll*w*((l+r)*(r-l+1)/2);
    tr[k].s.sii+=1ll*w*((r*(r+1)*(2*r+1)-(l-1)*l*(2*l-1))/6);
    tr[k].f+=w;
}
void down(int k)
{
    change((k<<1),tr[k].f);
    change((k<<1|1),tr[k].f);
    tr[k].f=0;
}
void build(int k,int l,int r)
{
    tr[k].l=l;tr[k].r=r;
    if(l==r)
    {
        tr[k].f=tr[k].s.si=tr[k].s.sii=tr[k].s.s=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    tr[k].s=tr[k<<1].s+tr[k<<1|1].s;
}
node ask(int k,int x,int y)
{
    //cout<<tr[k].l<<" "<<tr[k].r<<" "<<x<<" "<<y<<" "<<tr[k].s.s<<endl;
    int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    if(tr[k].f) down(k);
    if(x==l&&r==y) return tr[k].s;
    if(y<=mid) return ask(k<<1,x,y);
    else if(x>mid) return ask(k<<1|1,x,y);
    return ask(k<<1,x,mid)+ask(k<<1|1,mid+1,y);
}
void add(int k,int x,int y,int w)
{
    //cout<<k<<" "<<x<<" "<<y<<" "<<tr[k].s.s<<endl;
    int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    if(tr[k].f) down(k);
    if(x==l&&r==y)
    {
        change(k,w);
        return;
    }
    if(y<=mid) add(k<<1,x,y,w);
    else if(x>mid) add(k<<1|1,x,y,w);
    else add(k<<1,x,mid,w),add(k<<1|1,mid+1,y,w);
    tr[k].s=tr[k<<1].s+tr[k<<1|1].s;
}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
signed main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n=rd()-1,m=rd();ll ans=0,len,d;
    char op[2];
    build(1,1,n);
    for(int i=1,x,y,z;i<=m;i++)
    {
        scanf("%s",op);
        if(op[0]=='C')
        {
            x=rd(),y=rd()-1,z=rd();
            //cout<<i<<" "<<x<<" "<<y<<endl;
            add(1,x,y,z);
        //    cout<<ask(1,31,39).s<<endl;
            continue;
        }
        if(op[0]=='Q')
        {
            x=rd(),y=rd()-1;
        //    cout<<x<<" "<<y<<endl;
            node tmp=ask(1,x,y);
            ans=(tmp.si*(x+y)-tmp.sii-1ll*(x-1)*(y+1)*tmp.s);
            //cout<<ask(1,1,2).s<<endl;
            //cout<<tmp.s<<" "<<tmp.si*(x+y)<<" "<<tmp.sii<<" "<<(x-1)*(y+1)*tmp.s<<endl;
            len=1ll*(y-x+2)*(y-x+1)/2;
            if(ans==0) puts("0/1");
            else d=gcd(ans,len),printf("%lld/%lld\n",ans/d,len/d);
        }
    }
}
/*
g++ 1.cpp -o 1
./1
4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4
*/
View Code

 

转载于:https://www.cnblogs.com/starsing/p/11239139.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值