首页 > 其他分享 >C. Monoblock(贡献 子段) CF 1715C

C. Monoblock(贡献 子段) CF 1715C

时间:2022-09-01 16:33:15浏览次数:65  
标签:1715C ch idx 子段 int res 贡献 Monoblock define

题目:

​ 给出长度为n的序列,计算其所有子段的答案和\((\sum_{l=1}^{n}\sum_{r=l}^n g(l, r))\)。对于子段\([l, r]\)的计算公式\(g(l, r)\)=l到r之间合并后的块数。
合并:对于\(i\) \((2 <= n)\),若\(a_i = a_i-1\),他们可以被看做是同一块。

分析:

​ 可以通过\(n^2\)的暴力计算得到答案,但是这样做时间复杂度会非常高,所以我们要想办法优化。由于每次只对一个数值进行修改,所以很容易将思维转化到计算贡献上。我们一开始可以用\(O(n)\)来预处理出初始状态的答案,然后对于每一次的修改,我们要对其进行贡献的修正,若是原本是相同,改后是不相同,那就要加上贡献,若是原本不相同,改后相同,那就要减去贡献。对于一个位置上的修改,要考虑修改前后他与左右的关系。

实现:

​ 那么我们已经得到了一个思路,但是计算贡献又是一个难题。对于初始状态来说,假设现在整段都是1,那么他的贡献为\(\sum_{i=1}^{n}i\),那么当\(a_i\)和\(a_{i-1}\)不同的时候,\(l\)的取值范围是\([1, i - 1]\),\(r\)的取值范围是\([i,n]\),那么这对数对于\((i-1)*(n-i+1)\)个子段贡献+1。那么初始化就做好了。

​ 接下来看修改的部分,若\(i\)被修改的时候,分别讨论左和右的情况,计算其影响的子段数量,从而得到总贡献的增减情况。

#include <bits/stdc++.h>
    
using namespace std;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define all(x) x.begin(), x.end()
#define pb push_back
#define ios ios::sync_with_stdio(false);cin.tie(0);
#define debug(x)    cout << x << endl;
#define SZ(x)    (int)x.size()
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
void read(int &x) {int s = 0, f = 1; char ch = getchar(); while(!isdigit(ch)) {f = (ch == '-' ? -1 : f); ch = getchar();} while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();} x = s * f;}

const int N = 100005;
int n, m;
int a[N];
LL res = 0;

void init()
{
    for(int i = 2; i <= n; i ++)
        if(a[i - 1] != a[i])
            res += 1ll * (i - 1) * (n - i + 1);
}

signed main()
{
    scanf("%d%d", &n, &m);
    res = 1ll * n * (n + 1) / 2; //初始贡献
    for(int i = 1; i <= n; i ++)
        scanf("%d", &a[i]);
    init(); //初始化贡献

    while(m --)
    {
        int idx, val;
        scanf("%d%d", &idx, &val);
        if(idx > 1 && a[idx] != a[idx - 1])
            res -= 1ll * (idx - 1) * (n - idx + 1);
        if(idx < n && a[idx] != a[idx + 1])
            res -= 1ll * idx * (n - idx);
        a[idx] = val; 
        if(idx > 1 && a[idx] != a[idx - 1])
            res += 1ll * (idx - 1) * (n - idx + 1);
        if(idx < n && a[idx] != a[idx + 1])
            res += 1ll * idx * (n - idx);
        printf("%lld\n", res);
    }   
}

标签:1715C,ch,idx,子段,int,res,贡献,Monoblock,define
From: https://www.cnblogs.com/DM11/p/16646986.html

相关文章

  • CF1715C 题解
    前言题目传送门!更好的阅读体验?简单的数学题。思路每次只变一个数,因此考虑在短时间内计算:每个位置的数产生的贡献。容易发现以下的条件:不管\(a_i\)是什么,当它作......
  • CF1715C Monoblock 题解
    思路根据题意我们不难看出,求一个区间的块的数量即求区间内\(a_i\neqa_{i-1}\)的数量,如果直接枚举每个区间的话,时间复杂度是\(\mathcalO(n^2)\)显然这样做是不行的,但......
  • CodeForces-1715C Monoblock
    Monoblockdp先想想如何计算初始值\(dp[x]\)表示以第\(x\)个位置为\(r\),他的所有贡献状态转移:如果\(a_x=a_{x-1}\):\(dp[x]=dp[x-1]+1\),代表只增加了\(l......
  • 最大子段和动态规划
    【问题描述】八戒押x两银子,猫掌柜给定一个乱序数组arr,长度为N,有正数也有负数,正数表示赢钱,负数表示输钱。求arr的一个连续子数组,使得子数组的和最大,这样八戒才能尽可能......
  • 3.最大子段和之分治递归法(分治)
    题目描述:给定n(1<=n<=50000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,......