首页 > 其他分享 >CF878E 题解

CF878E 题解

时间:2023-07-11 14:12:59浏览次数:50  
标签:CF878E nums int 题解 sum 合并 st max

CF878E Numbers on the blackboard 题解

洛谷

Codeforces

Description

给出 \(n\) 个数字,每次询问一个区间 \([l,r]\),对这个区间内部的点进行如下操作。

  • 每次操作可以合并相邻两个数 \(x,y\),用 \(x+2y\) 替换它们。

对于每次询问,输出当最后只剩下一个数字时,这个数字的最大值。询问互相独立,答案对 \(10^9 + 7\) 取模。

Solution

我们考虑把整个区间从右向左依次合并答案是什么样的。

  • 第一次合并 \(nums_{r - 1} + 2 \times nums_{r}\)。
  • 第二次合并 \(nums_{r - 2} + 2 \times \left (nums_{r - 1} + 2 \times nums_{r} \right)\)。
  • \(\cdots\)
  • 最后一次 \(nums_{l} + 2 \times \left (nums_{l + 1} + 2\times ( nums_{l + 2} + \cdots )\right)\)

可以看出,对于每一个 \(nums_{i}\),对答案的贡献都是 \(2^{k} (k \geq 0)\),并且 \(\forall i \in [1,n-1]\),\(k_{i + 1} = k_{i} + 1\)。

那么如果我们不是这样合并的是否还有这样的性质呢?考虑把合并的区间分成从右向左按顺序合并的若干段,易证每段仍符合该性质。合并两段的时候会发生什么呢,可以想到是将右侧一段的贡献整个乘以 \(2\),也就是将右侧段的 \(k\) 全部加一。

因此,无论我们如何合并,最终对于整个区间每个数所乘的系数是一定的,系数变化如下(括号表示不一定存在)。即一个从 \(0\) 开始的串和一些从 \(1\) 开始的串。

\[0,1,\cdots,1,(2),\cdots,1,(2),\cdots \]

想让最终结果尽可能大,可以考虑贪心。

假设当前决策到第 \(i\) 个数,\(i - 1\) 所在的块长度为 \(len\)。

  • 若 \(nums_{i} < 0\),选择不合并,单独开出来一个块,系数为 \(2^{0} = 1\)。
  • 若 \(nums_{i} \ge 0\),选择合并,系数为 \(2^{len}\)。
  • 若合并后使得该块变为正的,代表后面的可以抵消中间负数的影响,继续向前合并。

可以看出上面的过程存储每一个块类似于栈,因此我们用栈来模拟这个过程。直接实现复杂度为 \(\Omicron(nq)\),我们可以离线操作,将操作按照 \(r\) 排序,再利用二分查找 \(l\) 右侧的第一个块,单独减去 \(l\) 左侧多出来的贡献即可。时间复杂度变为 \(O(n \log n)\)。合并过程中,如果遇到极大的块,不能直接继续合并,否则轻松炸 long long,特判这种情况,将这个块设为极大值即可。

Codes

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define max_n 410101
#define mo 1000000007
void read(int &p)
{
    int k = 1;
    p = 0;
    char c = getchar();
    while(c < '0' || c > '9')
    {
        if(c == '-')
        {
            k =  -1;
        }
        c = getchar();
    }
    while(c >='0' && c <= '9')
    {
        p = p * 10 + c - '0';
        c = getchar();
    }
    p *= k;
    return ;
}
void write_(int x)
{
    if(x < 0)
    {
        putchar('-');
        x = -x;
    }
    if(x > 9)
    {
        write_(x / 10);
    }
    putchar(x % 10 + '0');
}
void writesp(int x)
{
    write_(x);
    putchar(' ');
}
void writeln(int x)
{
    write_(x);
    puts("");
}
int n,m;
int lg2[max_n],nums[max_n],sum[max_n];
struct Query
{
    int l,r,id;
    bool operator<(Query q2)
    const {
        if(this->r != q2.r)
        {
            return this->r < q2.r;
        }
        else
        {
            return this->l > q2.l;
        }
    }
}querys[max_n];
int sum2[max_n],ans[max_n],pow_2[max_n],st[max_n],ans_sum[max_n],inv[max_n];
signed main()
{
    read(n),read(m);
    pow_2[0] = inv[0] = 1;
    for(int i = 1;i <= n;i++)
    {
        read(nums[i]);
        pow_2[i] = (pow_2[i - 1] << 1LL) % mo;
        inv[i] = inv[i - 1] * 500000004 % mo; // 预处理逆元

        ans_sum[i] = (ans_sum[i - 1] + pow_2[i] * nums[i]) % mo; // 预处理不分块贡献
    }
    for(int i = 1;i <= m;i++)
    {
        read(querys[i].l);       
        read(querys[i].r);
        querys[i].id = i;
    }
    stable_sort(querys + 1,querys + m + 1);// 按照右区间排序
    for(int i = 1,j = 1,l,r;i <= m;i++)
    {
        l = querys[i].l,r = querys[i].r;
        for(;j <= r;j++)
        {    
            st[++st[0]] = j;
            sum[st[0]] = nums[j];
            // 存在多余的块且当前块大于 0
            while(st[0] > 1 && sum[st[0]] > 0)
            {
                // 为了避免爆炸的数字大小,特殊处理一些情况
                if(st[st[0]] - st[st[0] - 1] >= 40 || (1LL << (st[st[0]] - st[st[0] - 1])) > (0x7fffff7f - sum[st[0] - 1])/ sum[st[0]])
                {
                    sum[st[0] - 1] = 0x7fffff7f;
                }
                else
                {
                    sum[st[0] - 1] += (1LL << (st[st[0]] - st[st[0] - 1])) * sum[st[0]];
                }
                --st[0];
            }
            if(sum[st[0]] < 0x7fffff7f)
            {
                sum2[st[0]] = (sum2[st[0] - 1] + sum[st[0]]) % mo;
            }
            else
            {
                sum2[st[0]] = ans_sum[j];
            }
        }
        // 末尾标记
        st[st[0] + 1] = r + 1;
        // l 区间右侧的块
        int pos = upper_bound(st + 1,st + st[0] + 2,l) - st;
        // 单独处理左侧答案
        (ans[querys[i].id] = ((sum2[st[0]] - sum2[pos - 1]) * 2 + ((ans_sum[st[pos] - 1] - ans_sum[l - 1]) * inv[l]) % mo ) % mo+ mo) %= mo;
    }
    for(int i = 1;i <= m;i++)
    {
        writeln(ans[i]);
    }
    return 0;
}

标签:CF878E,nums,int,题解,sum,合并,st,max
From: https://www.cnblogs.com/yuhang-ren/p/17544471.html

相关文章

  • CODE FESTIVAL 2017 Final J 题解
    problem&blog。萌萌点分治,积累个trick/qq。对于完全图\((V,E)\),将\(E\)分成\(E_1,E_2,\cdots,E_k\)(\(E_1\cupE_2\cup\cdots\cupE_k=E\))。对每个边集求MST,得到新边集\(E_1^{'},E_2^{'},\cdots,E_k^{'}\),再求MST。最终剩下的边集,等同于原边集的MST。......
  • P4016题解
    本题是一个比较经典的问题(环形均分纸牌问题),我也不知道为什么它在网络流24题里面出现。但是作为一道比较典的排序算法的题,还是放出来讲一下。solution假设\(a_1\)给\(a_n\)了\(x_1\)张纸牌,\(a_2\)给\(a_1\)了\(x_2\)张纸牌......(\(x_i\)可正可负)。因此操作数量为......
  • P2886题解
    题目大意给定起点\(S\)和终点\(T\),求从起点到终点恰好经过\(N\)(\(N\)给定)条边的最短路径。solution发现本题点数巨多,但是边数小到可怜,我们可以只保留了一部分点,先判断连通性,不连通直接输出即可。当\(S\)和\(T\)连通时,我们设\(G^k\)为经过\(k\)条边后最短路的邻......
  • Largest-Smallest-Cyclic-Shift题解
    题目链接本题码量不大,但是事实上是一道极其难想的思维题。题目描述你有\(a\)个a,\(b\)个b,\(c\)个c。要求用这\(a+b+c\)个字母拼接成一个字符串,使得这个字符串的最小表示法在所有能拼成的字符串中最大。补充:最小表示法,将一个字符串的最后一个字符放到首位,重复这个操作,......
  • AT-abc214-g题解
    题目描述给定两个排列\(p,q\),要求统计满足\(\foralli,r_i\not=p_i,r_i\not=q_i\)的排列\(r\)的数量。对\(1000000007\)取模数据范围\(n\le3000\)。solution本题要求统计数量,反正我想了半天没想到怎么正向统计(bushi),因此我们考虑容斥。设\(h_i\)为只看其中......
  • P3599题解
    本题是一道比较典的构造题,拿来扩宽扩宽大家的眼界吧。Task1试判断能否构造并构造一个长度为\(n\)的\(1\simn\)的排列,满足其\(n\)个前缀和在模\(n\)的意义下互不相同。很容易想到的一点是:\(n\)一定排在第一位,因为如果排在别的位,加上\(n\)后在模\(n\)意义下是不......
  • CF1421E题解
    题目链接本题作为一道本人思考了50分钟没想出来的大思维题,我觉得可以用来扩宽一下大家的视野。本题中,我们每次都会选取两个相邻的数\(a_i\)和\(a_{i+1}\),同时将这两位变为一位,这一位上填的数为\(-(a_i+a_{i+1})\)。很容易想到的一个\(O(n^3)\)的dp做法是区间dp,设\(f[......
  • NOIP2013-2023题解
    本文章主要是为了不想卷题的时候不是特别颓废而准备本文章是为了总结NOIP最近的题目(为了今年NOIP做准备),目前还没写完,尽量做的全面一些。2013积木大赛给定一个长度为\(n\)的序列\(h_i\),初始有一个全为\(0\)的序列,每次操作可以任意选择\(L,R\),使得\([L,R]\)这段区......
  • CF1545D-题解
    题目链接题目描述有\(n\)个人和\(k\)个间隔相同时间的时刻,每个人都向正方向做匀速直线运动。给出每个时刻(\(0\simk-1\))的所有人的坐标集合(无序),在这\(nk\)个数中有一个数是错误的,找出这个错误的数并将其改正。数据范围:\(5\len\le1000\),\(7\lek\le1000\)。加......
  • 【问题解决】docker login报错 org.freedesktop.Secret.Error.IsLocked: Cannot creat
    问题场景环境docker24.0.2社区版UbuntuServer18.04LTS刚刚执行dockerlogin登录仓库报错:hellxz@bigdata:~/dockerTest$dockerloginharbor.xxx.com.cnUsername:hellxzPassword:**Message:17:26:21.611:Remoteerrorfromsecretservice:org.freedesktop.Se......