zoukankan      html  css  js  c++  java
  • 【HAOI2012】高速公路

    题面

    https://www.luogu.org/problem/P2221

    题解

    其实我认为这道题还挺难的。。。。虽然是道水题罢了。

    这道题让我自闭了两次,可见我现在学习状态之差,整个题从思考到写出来竟然花了下午的一个小时和一个晚上的时间。

    马上的$NOIP$,如果还是以这种状态,肯定是考不好的。所以这一个月一定要大刀阔斧的把自己心中的浮躁之火灭下去。

    首先,一看到这个形式我就想到考虑贡献,其实这是$fake$的,应该是一个线段树维护分治信息。

    分治维护的信息有哪些呢?

    • 答案肯定要。
    • 合并两个区间的时候还需要算一个阶梯一样的前缀和,总和肯定也要
    • 区间的$siz$

    下列是注意事项:

    • 一开始算错答案的最大值,以为要开$\_\_int128$,事实上,答案的最大值为$$10000 sum_{i=1}^{n}{i(n-i)}=10000(frac{n(n+1)^{2}}{2}-frac{n(n+1)(2n+1)}{6}).$$(什么,你问证明,公式在合肥八中楼梯上写着呢,证明$syj$都只会用归纳法我怎么可能知道,翻《具体数学》去吧),是$10$的$19$次方级别的数,如果算上一个$frac{1}{6}$的常数,大概是能够的。。。。。
    • $node$结构体,表示一个区间的分治信息,我们需要一个合并的函数,在合并的函数中,不仅要维护$sl,sr,s,ans$,还要维护$siz$和$tag$(和答案关系不大,容易忘啊),事实上,$tag$不应该出现在上面,它应该出现在线段树上面,这样是更简练的,但我没有改。
    • 瞪大狗眼好好看看,贡献是加的,我算的时候想当然,以我以前水题的经验,不加思考的乘了起来。。。。。。
    • 事实上,$sl$和$sr$的含义也是容易弄混淆的,要看怎么定义的,我们最后需要的是一个二阶前缀和,阶梯的话可能弄反了。。
    • 函数返回值为自定义结构体的时候记得把$ret$$return$回去,不然的话会出现奇怪的很大的值。
    • 对于$merge$函数不太好定义一个单位$node$,那线段树就换一种写法,在向下递归的时候就判断,不交不向下。
    • 老生常谈的东西了,向下递归之前$pushdown$,$pushdown$的时候记得下传$tag$($pushdown$操作可以把区间修改完全覆盖的代码复制过去,然后把$x$改称$ls$和$rs$)。
    • 算等差数列前$n$项和,记得除二($syj$:揍死你)
    • 乘的项数比较多,保险的做法是每乘一个就在中间乘一个$1LL$
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define ri register int
    #define N 100050
    #define LL long long
    using namespace std;
    
    int n,m;
    struct node {
      int siz;
      LL sl,sr,s;
      LL ans;
      int ts;
    } t[N<<2];
    
    #define ls (x<<1)
    #define rs (x<<1|1)
    
    void maketree(int x,int lb,int rb) {
      t[x].siz=rb-lb+1;
      t[x].sl=t[x].sr=t[x].s=t[x].ans=0;
      t[x].ts=0;
      if (lb==rb) return;
      int mid=(lb+rb)>>1;
      maketree(ls,lb,mid); maketree(rs,mid+1,rb);
    }
    
    node merge(node a,node b) {
      node ret;
      ret.ans=a.sr*b.siz+b.sl*a.siz+a.ans+b.ans;
      ret.s=a.s+b.s;
      ret.sl=a.sl+b.siz*a.s+b.sl;
      ret.sr=b.sr+a.siz*b.s+a.sr;
      ret.siz=a.siz+b.siz;
      ret.ts=0;
      return ret;
    }
    
    void pushdown(int x) {
      int k=t[x].ts;
      t[x].ts=0;
      t[ls].sl+=(LL)((1+t[ls].siz)*1LL*t[ls].siz)/2*1LL*k;
      t[ls].sr+=(LL)((1+t[ls].siz)*1LL*t[ls].siz)/2*1LL*k;
      t[ls].s+=(LL)(t[ls].siz)*1LL*k;
      t[ls].ans+=((LL)(t[ls].siz+1)*1LL*(t[ls].siz+1)*1LL*t[ls].siz/2-(LL)(t[ls].siz*1LL*(t[ls].siz+1)*1LL*(2*t[ls].siz+1))/6)*1LL*k;
      t[ls].ts+=k;
    
      t[rs].sl+=(LL)((1+t[rs].siz)*1LL*t[rs].siz)/2*k;
      t[rs].sr+=(LL)((1+t[rs].siz)*1LL*t[rs].siz)/2*k;
      t[rs].s+=(LL)(t[rs].siz)*1LL*k;
      t[rs].ans+=((LL)(t[rs].siz+1)*1LL*(t[rs].siz+1)*1LL*t[rs].siz/2-(LL)(t[rs].siz*1LL*(t[rs].siz+1)*1LL*(2*t[rs].siz+1))/6)*1LL*k;
      t[rs].ts+=k;
    }
    
    void modify(int x,int lb,int rb,int l,int r,int v) {
      if (l<=lb && rb<=r) {
        t[x].sl+=(LL)((1+t[x].siz)*1LL*t[x].siz)/2*1LL*v;
        t[x].sr+=(LL)((1+t[x].siz)*1LL*t[x].siz)/2*1LL*v;
        t[x].s+=(LL)(t[x].siz)*1LL*v;
        t[x].ans+=((LL)(t[x].siz+1)*1LL*(t[x].siz+1)*1LL*t[x].siz/2-(LL)(t[x].siz*1LL*(t[x].siz+1)*1LL*(2*t[x].siz+1))/6)*1LL*v;
        t[x].ts+=v;
        return;
      }
      if (lb>r || rb<l) {
        return;
      }
      pushdown(x);
      int mid=(lb+rb)>>1;
      modify(ls,lb,mid,l,r,v); modify(rs,mid+1,rb,l,r,v);
      t[x]=merge(t[ls],t[rs]);
    }
    
    node query(int x,int lb,int rb,int l,int r) {
      if (l<=lb && rb<=r) {
        return t[x];
      }
      pushdown(x);
      int mid=(lb+rb)>>1;
      if (r<=mid) return query(ls,lb,mid,l,r);
      if (l>mid) return query(rs,mid+1,rb,l,r);
      return merge(query(ls,lb,mid,l,r),query(rs,mid+1,rb,l,r));
    }
    
    LL gcd(LL a,LL b) {
      if (b==0) return a;
      return gcd(b,a%b);
    }
    
    int main() {
      char opt[5];
      scanf("%d %d",&n,&m); n--;
      maketree(1,1,n);
      for (ri i=1,l,r,v;i<=m;i++) {
        scanf("%s",opt);
        if (opt[0]=='Q') {
          scanf("%d %d",&l,&r); r--;
          node ret=query(1,1,n,l,r);
          LL tms=(r-l+1)*(LL)(r-l)/2+r-l+1;
          LL d=gcd(tms,ret.ans%tms);
          long long a=(LL)(ret.ans/d),b=(LL)(tms/d);
          printf("%lld/%lld
    ",a,b);
        }
        else {
          scanf("%d %d %d",&l,&r,&v); r--;
          modify(1,1,n,l,r,v);
        }
      }
      return 0;
    }
  • 相关阅读:
    PowerDesigner中利用数据库表反向生成PDM(jdk必须是32位)
    Struts2 Web Project 实现中文、英语的切换
    一张图解决Struts2添加源码
    Struts2配置文件struts.xml的编辑自动提示代码功能
    Hibernate多对一(注解)
    SQL Server 日期和时间函数
    ORACLE日期时间函数大全
    ORACLE中函数MONTHS_BETWEEN的使用
    SQL经典面试题及答案
    SQL数据库面试题以及答案
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11657941.html
Copyright © 2011-2022 走看看