HAOI2012高速公路bzoj2752 (线段树,数学)

题目大意:

给定一个长度为n的链,一共m次操作

对于每次操作

\(C\ l\ r\ x\)表示将第l个点到第r个点之间的所有道路的权值增加v

\(Q\ l\ r\)在第l个到第r个点里等概率随机取出两个不同的点a和b,那么从a行驶到b将期望花费多少费用呢

QwQ我们可以考虑将期望分为分子和分母两部分

首先考虑分母,分母就是在\(r-l+1\)个点中选两个点的方案数,也就是\({r-l+1}\choose 2\)

而分子就是总权值了

对于一个在\([l,r]\)的点\(i\)来说

它会被计算\((i-l+1)\times (r-i+1)\)次 (就跟分别在左右两个区间枚举端点是一样的)

那么我们求的东西也就变成了

\(\sum_{i=l}^{r} w[i]\times(i-l+1)\times(r-i+1)\)

我们考虑将它展开:

\(\sum_{i=l}^{r} w[i]\times((r+l)\times i-i\times i +(r-l+1-r\times l))\)

再进一步来一波,对于区间\([l,r]\)我们就是求的是:

\((r+l)\times \sum_{i=l}^{r}w[i]\times i + (r-l+1-r\times l)\times \sum_{i=l}^{r}w[i] + \sum_{i=l}^{r}w[i]*i^2\)

而对于更新,我们也不难找出一些规律了咯

所以对于一个区间,我们只需要维护一个\(w[i],w[i]*i,w[i]*i^2,i^2,i\)的sigma值就能处理更新和合并了!

void up(int root)
{
    f[root].si=f[2*root].si+f[2*root+1].si;
    f[root].sii=f[2*root].sii+f[2*root+1].sii;
    f[root].scostii=f[2*root].scostii+f[2*root+1].scostii;
    f[root].scosti=f[2*root].scosti+f[2*root+1].scosti;
    f[root].scost=f[2*root].scost+f[2*root+1].scost;
}

void pushdown(int root,int l,int r)
{
   ll mid = (l+r) >> 1;
   if (add[root])
   {
      add[2*root]+=add[root];
      add[2*root+1]+=add[root];
      f[2*root].scost+=add[root]*(mid-l+1);
      f[2*root+1].scost+=add[root]*(r-mid);
      f[2*root].scosti+=add[root]*f[2*root].si;
      f[2*root+1].scosti+=add[root]*f[2*root+1].si;
      f[2*root].scostii+=add[root]*f[2*root].sii;
      f[2*root+1].scostii+=add[root]*f[2*root+1].sii;
      add[root]=0;
   }
}

emmmmm

query的时候也记得\(\times\)的时候要乘询问的总区间,而不是当然区间!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#define ll long long
using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
  return x*f;
}

const int maxn = 1e5+1e2;

struct Node{
   ll scost,scosti,scostii,si,sii;
   ll ans;
};

Node f[4*maxn];
ll add[4*maxn];
int n,m;

ll count(int xx,int yy)
{
    ll x=xx,y=yy;
    return (y-x+1)*(y-x+2)/2;
}

void up(int root)
{
    f[root].si=f[2*root].si+f[2*root+1].si;
    f[root].sii=f[2*root].sii+f[2*root+1].sii;
    f[root].scostii=f[2*root].scostii+f[2*root+1].scostii;
    f[root].scosti=f[2*root].scosti+f[2*root+1].scosti;
    f[root].scost=f[2*root].scost+f[2*root+1].scost;
}

void pushdown(int root,int l,int r)
{
   ll mid = (l+r) >> 1;
   if (add[root])
   {
      add[2*root]+=add[root];
      add[2*root+1]+=add[root];
      f[2*root].scost+=add[root]*(mid-l+1);
      f[2*root+1].scost+=add[root]*(r-mid);
      f[2*root].scosti+=add[root]*f[2*root].si;
      f[2*root+1].scosti+=add[root]*f[2*root+1].si;
      f[2*root].scostii+=add[root]*f[2*root].sii;
      f[2*root+1].scostii+=add[root]*f[2*root+1].sii;
      add[root]=0;
   }
}

void build(int root,int l,int r)
{
    if (l==r)
    {
        f[root].si=(long long)1LL*l;
        f[root].sii=(long long) 1LL*l*l;
        return;
    }
    int mid = (l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}

void update(int root,int l,int r,int x,int y,ll p)
{
    if (x<=l && r<=y)
    {
        ll len=r-l+1;
        f[root].scost+=1LL*p*len;
        f[root].scosti+=1LL*p*f[root].si;
        f[root].scostii+=1LL*p*f[root].sii;
        add[root]+=p;
        return;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    if (x<=mid) update(2*root,l,mid,x,y,p);
    if (y>mid) update(2*root+1,mid+1,r,x,y,p);
    up(root);
}

ll query(int root,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        ll xx = x,yy=y;
        return (long long)1LL*(xx+yy)*f[root].scosti+(long long)1LL*(yy-xx+1-xx*yy)*f[root].scost-f[root].scostii;
    }
    pushdown(root,l,r);
    int mid = (l+r) >> 1;
    ll ans=0;
    if (x<=mid) ans+=query(2*root,l,mid,x,y);
    if (y>mid) ans+=query(2*root+1,mid+1,r,x,y);
    return ans;
}

int main()
{
  scanf("%d%d",&n,&m);
  n--;
  build(1,1,n);
  for (int i=1;i<=m;i++)
  {
     char s[10];
     scanf("%s",s+1);
     if (s[1]==‘C‘)
     {
        int x,y;
        ll z;
        scanf("%d%d%lld",&x,&y,&z);
        y--;
        update(1,1,n,x,y,z);
       }
     if (s[1]==‘Q‘)
     {
        int x,y;
        scanf("%d%d",&x,&y);
        y--;
        ll tmp=query(1,1,n,x,y);
        ll tmp1=count(x,y);
        printf("%lld/%lld\n",tmp/__gcd(tmp1,tmp),tmp1/__gcd(tmp1,tmp));
     }
  }
  return 0;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10160724.html

时间: 2024-10-11 07:27:11

HAOI2012高速公路bzoj2752 (线段树,数学)的相关文章

【BZOJ2752】[HAOI2012]高速公路(road) 线段树

[BZOJ2752][HAOI2012]高速公路(road) Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用.高速路刚建成时所有的路段都是免费的.政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政

BZOJ 2752: [HAOI2012]高速公路(road) [线段树 期望]

2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1219  Solved: 446[Submit][Status][Discuss] Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1

BZOJ 2752 [HAOI2012]高速公路(road) 线段树

题意:链接 方法:线段树 解析: 这道题刚开始脑抽想了个O(n/2*(logn)*m)的脑抽算法就写上交了=-= 然而当时我神奇地在草纸上写了个O(sqrt(n)/2*(logn)*m).. 当时在想另一个什么分块的什么东西就没过大脑=-= 然后开始找规律呗. 不妨把权值安到点上. 之后考虑一个点的贡献是什么. 其左边有多少个点,右边有多少个点的乘积. 这很显然啊,就是在枚举大长线段的左右端点啊.. 所以假设某个点的权值是val 那么就是val[i](i-x+1)(y-i+1)对吧. 之后就是展

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

HDU1394 Minimum Inversion Number 线段树+数学

Problem Description The inversion number of a given number sequence a1, a2, -, an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, -, an, if we move the first m >= 0 numbers to the end of the

【Luogu】P2221高速公路(线段树乱搞)

题目链接 这题……我从一开始就想歪了qwq. 为了缅怀逝去的一小时我就把我的30分暴力思路放上来. 首先我们观察枚举的区间.假设我们要枚举的范围是1~5之间的四条路,为了方便我们把它们叫做abcd. 那么观察我们枚举的区间. a ab abc abcd b bc bcd c cd d 观察有一些区间是可以合起来的. 然后观察a出现4次,b出现6次,c出现6次,d出现4次. 是有一定规律的qwq 然后就$\frac{nm}{2}的复杂度搞搞 就三十分 正确思路是,观察一条路选不选上(设这条路左点x

CF446C DZY Loves Fibonacci Numbers 线段树 + 数学

code: #include <bits/stdc++.h> #define N 400004 #define LL long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace

BZOJ 2752:[HAOI2012]高速公路(road)(线段树)

[HAOI2012]高速公路(road) Description Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站.Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用.高速路刚建成时所有的路段都是免费的.政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价.无聊的

JZYZOJ1527 [haoi2012]高速公路 线段树 期望

http://172.20.6.3/Problem_Show.asp?id=1527 日常线段树的pushdown写挂,果然每次写都想得不全面,以后要注意啊--求期望部分也不熟练,和平均数搞混也是orz,我已经是个期望都求不出来的废人了.这道题显然(大概)每个段的贡献是val[i]*(y-i+1)*(i-x+1);整体来说算是一看就是线段树的题. 代码 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring>