zoukankan      html  css  js  c++  java
  • 接毒瘤

    【题目描述】
    24OI的成员跑到了一片大森林,目的是摘一些毒瘤回去给高一同学吃。
    于是在毒瘤树下,cyl沿着一条线钉了N个木桩,并且在相邻两个木桩之间结网,初始时所有的网中都没有毒瘤。
    之后,树上可能会掉下毒瘤,cyl也可能拿走一部分毒瘤,我们统一把它们称为“操作”。
    在等毒瘤落下的cyl同学正在思考一个问题:对于给定的l,r(l<r),在第l个木桩到第r个木桩里等概率随机取出两个不同的木桩a和b,那么从min(a,b)到max(a,b)之间的所有的网中的毒瘤总数期望有多少呢?


    【输入输出格式】
    输入格式:
    第一行2个正整数N,M,表示有N个木桩,M个操作或询问。
    接下来M行,每行将出现以下两种形式中的一种:
    C l r v
    如果v>=0,表示第l个木桩到第r个木桩之间的每个网中都接到了v个毒瘤;
    否则表示cyl从第l个木桩到第r个木桩之间的每个网中都拿走了v个毒瘤;
    Q l r
    表示对于给定的l,r,要求回答cyl的问题;
    所有操作或询问中保证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 =20 =20
    3 =100 =100
    4 =300 =300
    5 =350 =350
    6 =2000 =2000
    7 =5000 =5000
    8 =10000 =10000
    9 =50000 =50000
    10 =100000 =100000

    首先得知道期望是什么:就是该区间内 a, b 的每一种情况所对应的毒瘤数之和除以情况数目。

    对了,题中说是两个木桩之间一个网兜,这么做不太方便,我们默认第i个网兜就属于这个网兜左边连着的木桩。

    所以对于所有的r,都应r--。

    接着说期望,比如区间2到5,那么总情况数就是 [2, 2], [2, 3], [2, 4], [2, 5], [3, 3], [3, 4], [3, 5], [4, 4], [4, 5], [5, 5]。

    那么对于长度为 L (L = r - l + 1)的区间,总情况数就是1 + 2 + 3 + 4 + …… + L。

    因此只要预处理一个前缀和数组就能解决分母了。

    接下来是分子,即每一个情况所对应的毒瘤数之和。

    我们可以换一种思路,去求网兜i在 l 到r区间里总共出现了多少次。

    于是易得

        次数 = (i - l + 1) * (r  - i + 1) * w[i]  (1)

    w[i]为当前网兜 i 里的毒瘤数

    于是

        总次数 = sum((i - l +1) * (r - i + 1) * w[i])  (i : l -> r)

    对于(1), 当我们把括号打开后

      次数 = - i * i * w[i] + (r + l) * i * w[i] + (-l * r - l + r + 1) * w[i]

    于是线段树就派上用场了,开一个线段树,同时维护sum1(w[i]), sum2(i * w[i]), sum3(i * i * w[i]).

    对于sum1(w[i]),就是普通的区间求和。

    对于sum2(i * w[i]),若是区间每一个数加 v,则这个区间的sum2就增加了(l + (l + 1) + (l + 2) + …… + r)。于是我们可以预处理一个前缀和(其实和分母的前缀和一样)。

    对于sum3(i * i * w[i]),若是区间每一个数加v,则这个区间sum3就增加了(l ^ 2 + (l + 1) ^ 2 + (l + 2) ^ 2 +…… + r ^ 2)。于是同样可以预处理一个i ^ 2的前缀和。

    综上,一个lazy[],三个sum[].

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<cmath>
      6 using namespace std;
      7 typedef long long ll;
      8 const int maxn = 2e5 + 5;
      9 
     10 int n, m;
     11 ll sigma1[maxn], sigma2[maxn];
     12 ll ans1 = 0, ans2 = 0, ans3 = 0;
     13 
     14 void init(int n)
     15 {
     16     for(ll i = 1; i <= n; ++i)
     17     {
     18         sigma1[i] = sigma1[i - 1] + i;
     19         sigma2[i] = sigma2[i - 1] + i * i;
     20     }
     21 }
     22 
     23 struct Tree
     24 {
     25     int l, r;
     26     ll sum1, sum2, sum3;        //w[i], i * w[i],i * i * w[i]
     27     int lazy;
     28 }tree[maxn << 2];        
     29 void build(int L, int R, int now)
     30 {
     31     tree[now].l = L; tree[now].r = R;
     32     if(L == R) return;
     33     int mid = (L + R) >> 1;
     34     build(L, mid, now << 1);
     35     build(mid + 1, R, now << 1 | 1);
     36 }
     37 void update(int L, int R, int now, int v)
     38 {
     39     if(tree[now].l == L && tree[now].r == R)
     40     {
     41         tree[now].lazy += v; 
     42         return;
     43     }
     44     tree[now].sum1 += (ll) v * (R - L + 1);
     45     tree[now].sum2 += (ll) v * (sigma1[R] - sigma1[L - 1]);
     46     tree[now].sum3 += (ll) v * (sigma2[R] - sigma2[L - 1]);
     47     int mid = (tree[now].l + tree[now].r) >> 1;
     48     if(R <= mid) update(L, R, now << 1, v);
     49     else if(L > mid) update(L, R, now << 1 | 1, v);
     50     else {update(L, mid, now << 1, v); update(mid + 1, R, now << 1 | 1, v);}
     51 }
     52 void pushdown(int now)
     53 {
     54     if(tree[now].lazy)
     55     {
     56         tree[now].sum1 += (ll) tree[now].lazy * (tree[now].r - tree[now].l + 1);
     57         tree[now].sum2 += (ll) tree[now].lazy * (sigma1[tree[now].r] - sigma1[tree[now].l - 1]);
     58         tree[now].sum3 += (ll) tree[now].lazy * (sigma2[tree[now].r] - sigma2[tree[now].l - 1]);
     59         tree[now << 1].lazy += tree[now].lazy;
     60         tree[now << 1 | 1].lazy += tree[now].lazy;
     61         tree[now].lazy = 0;
     62     }
     63 }
     64 void query(int L, int R, int now)
     65 {
     66     pushdown(now);
     67     if(tree[now].l == L && tree[now].r == R)
     68     {
     69         ans1 += tree[now].sum1;
     70         ans2 += tree[now].sum2;
     71         ans3 += tree[now].sum3;
     72         return;
     73     }
     74     int mid = (tree[now].l + tree[now].r) >> 1;
     75     if(R <= mid) query(L, R, now << 1);
     76     else if(L > mid) query(L, R, now << 1 | 1);
     77     else {query(L, mid, now << 1); query(mid + 1, R, now << 1 | 1);}
     78 }
     79 ll gcd(ll x,ll y) 
     80 {
     81     return y ? gcd(y, x % y) : x;
     82 }
     83 
     84 int main()
     85 {
     86     scanf("%d%d", &n, &m); 
     87     init(n);
     88     build(1, n, 1);
     89     for(int i = 1; i <= m; ++i)
     90     {
     91         char c; int L, R;
     92         cin >> c; scanf("%d%d", &L, &R);
     93         R--;
     94         if(c == 'C')
     95         {
     96             int v; scanf("%d", &v);
     97             update(L, R, 1, v);
     98         }
     99         else
    100         {
    101             ans1 = ans2 = ans3 = 0;
    102             query(L, R, 1);
    103             ll Upans = (R - L - (ll)L * R + 1) * ans1 + (R + L) * ans2 - ans3;
    104             ll Downans = sigma1[R - L + 1];
    105             ll div = gcd(Upans, Downans);
    106             printf("%lld/%lld
    ", Upans / div, Downans / div);
    107         }
    108     }
    109     return 0;
    110 }
  • 相关阅读:
    在Android中通过导入静态数据库来提高应用第一次的启动速度
    《sqlite权威指南》读书笔记 (一)
    Android APK反编译详解(附图)
    Android如何防止apk程序被反编译
    PopupWindow 学习总结
    Android开源框架Afinal第一篇——揭开圣女的面纱
    教程] 《开源框架-Afinal》之FinalHttp 01一步一脚
    android 下改变默认的checkbox的 选中 和被选中 图片
    Android设置RadioButton在文字的右边
    Android Selector 与 Shape 基本用法
  • 原文地址:https://www.cnblogs.com/mrclr/p/9005601.html
Copyright © 2011-2022 走看看