前缀和和差分的思想

一,前缀和

1,前缀和思想

  • 新定义性: 可以对需要前缀和的条件和数目进行取舍if (...) a[i]= a[i-1] + ..
  • 叠加性:在有序的数组标的前提下,二分就能取得第k大的数,注意和其他计数的结合
  • 注意大数据范围
  • 数学性: 求和求积运算的一切都可以实现

例一:给定一个由大写字母构成的字符串 s,请计算其中有多少个子序列 QAQ。注意,子序列不需要连续。

  • 分析:
    回文性质, 固定 A 的位置,找到前后有多少 Q ,乘法原理即可
    找 Q 的过程就是新定义的典例
int n,ans;
string s;
int t[N];
int main()
{
    cin>>s;
    n= s.size();
    s = ' ' + s;
    for (int i = 1; i <= n; i ++ ) t[i]= t[i-1]+(s[i]=='Q');
    for (int i = 2; i < n; i ++ ) if(s[i]=='A')ans+= t[i-1]*(t[n]-t[i]);
    cout<<ans;
}

2,多维前缀和

容斥求法:

for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

叠加求法:

  • 二维前缀和
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]+=f[i][j-1];//第一次 

for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]+=f[i-1][j];//第二次
  • 三维前缀和(多维亦然)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=l;k++)
f[i][j][k]+=f[i][j][k-1];//第一次 

for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=l;k++)
f[i][j][k]+=f[i][j-1][k];//第二次 

for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=l;k++)
f[i][j][k]+=f[i-1][j][k];//第三次 
  • 对非这一维的所有维度枚举殆尽!
  • 这一维度的信息只和这一维度上的递推有关
  • 注意顺序,保证改变的维度从小到大

拓展:(多维超立方体求和)

给你一个长度为 2 n 2^n 2n 的序列 a a a,每个 1 ≤ K ≤ 2 n − 1 1\le K\le 2^n−1 1K2n1,找出最大的 a i + a j a_i+a_j ai+aj i o r j i \mathbin{\mathrm{or}} j iorj 0 ≤ i < j < 2 n 0 \le i < j < 2^n 0i<j<2n)并输出。

  • 可以把转换:
  • i ∣ j ⫅ K i|j\subseteqq K ijK 的问题,然后取每一个小于 K K K 的集合里最大值即可。(难点)
  • 考虑把每一个二进制位作为下标,取得最大值操作变成高维前缀信息(集合最小和次小)维护(每一维只有01)
  • 换句话说:题目中转移的集合就是比 k k k 小的所有二进制数
int main()
{
    cin >> n ;
    rep(i,0,(1<<n)-1) cin >> f[i].x;
    rep(i,0,n-1)
    {
        rep(j,0,(1<<n)-1) if(j&(1<<i))
        {
            int m1 = f[j].x;
            int m2 = f[j].y;
            int m3 = f[j^(1<<i)].x;
            int m4 = f[j^(1<<i)].y;
            if(m3>=m1) m2 = m1 ,m1 = m3;
            else if(m3>m2) m2 = m3; 
            else if(m4>m2) m2= m4;
            f[j].x = m1;
            f[j].y = m2;
        }
    }
    
    rep(i,1,(1<<n)-1) 
    {
        ans = max(ans,f[i].x+f[i].y);
        cout << ans << endl;
    }
    
    return 0;
}

二,差分思想

  • 作用持续: 单点修改的目的是在往后的数据上造成一个扰动,这个扰动只要没用被新的单点修改扰动抵消,在前缀(…)这种具有积累效应的运算上就会持续存在
  • 作用形式:差分数组,懒标记,异或运算

具体而言:
1,需要开始去区间操做的地方在差分数组上打上(作用记号),作用形式不限,叠加效应不能忽视
2,注销某一种操作是,在差分数组上打上(注销记号),注销这种操作

例一:(可能是个模拟?或者是降维)

在这里插入图片描述

差分套差分,跳水的影响具有周期波动和规律性,第一层差分表示影响范围,第二层表示具体影响度,初始均为0
delta 人为处理偏移量,在数组下标下处理横坐标负数

#define rep(i,j,k) for(int i= j;i<=k;i++)
int c[N],s[N];
int n,m;
signed main()
{
    int delta = 1000000;
    scanf("%d%d",&m,&n);
    while (m--)
    {
        int v,x;
        scanf ("%d%d",&v,&x);
        c[x- 3*v+1 +delta]++;
        c[x- 2*v+1 +delta]-= 2;
        c[x +1 +delta]+= 2;
        c[x+ 2*v+1 +delta]-= 2;
        c[x+ 3*v+1 +delta]++;
    }

    rep(i,0,delta+n) c[i]+= c[i-1] , s[i]= s[i-1]+c[i];
    rep(i,delta+1,delta+n)printf("%d ",s[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流苏贺风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值