前缀和问题

下面笔者贴出学习大佬的博客地址:
前缀和问题前缀和、二维前缀和与差分的小总结二维前缀和详解

接下来是一个例题的参考博客:2019中山大学ACM校赛:二维前缀和二维差分
贴出上面博客的注释代码:

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<vector>
using namespace std;
//const int Max = 1e4; 这里使用会超出内存限制 
//int record[Max][Max], glass[Max][Max];
int main(){
  int n, m;
  while(scanf("%d%d", &n, &m) == 2){
    int mo, th;
    //memset(record, 0, sizeof(record));
    //memset(glass, 0, sizeof(glass));
    vector<vector<int> > record(n + 2, vector<int>(m + 2)), glass(n + 2, vector<int>(m + 2));
    //cin>>mo;使用这个比使用scanf更慢,所以这里会超时 
    scanf("%d", &mo);
    int ldx, ldy, rux, ruy;
    while(mo--){
      scanf("%d%d%d%d", &ldx, &ldy, &rux, &ruy);
      ++record[ldx][ldy];
      ++record[rux+1][ruy+1];
      --record[ldx][ruy+1];
      --record[rux+1][ldy];
    }
    for(int i = 1; i <= n; i++){
      for(int j = 1; j <= m; j++){
        record[i][j] += record[i-1][j] + record[i][j-1] - record[i-1][j-1];
      }
    }
    for(int i = 1; i <= n; i++){
      for(int j = 1; j <= m; j++){
        glass[i][j] += glass[i-1][j] + glass[i][j-1] - glass[i-1][j-1] + (record[i][j] > 0 ? 1 : 0);
      }
    }
    //cin>>th;
    scanf("%d", &th);
    while(th--){
      scanf("%d%d%d%d", &ldx, &ldy, &rux, &ruy);
      int ans = glass[rux][ruy] - glass[ldx-1][ruy] - glass[rux][ldy-1] + glass[ldx-1][ldy-1];
      if(ans == (rux - ldx + 1)*(ruy - ldy + 1)){
        cout<<"YES"<<endl;
      }else{
        cout<<"NO"<<endl;
      }
    }
  }
  return 0;
}

接下来列出在寒假牛客集训时的一道题目:关于字符串和前缀和问题
和在题解看到的大佬写的代码:大佬的题解

#include<iostream>
#include<string.h>
using namespace std;
int dp[200010][26] = {0};
int main(){
  int n, k, i, j;
  cin>>n>>k;
  string s;
  cin>>s;
  dp[0][s[0]-'a'] = 1;
  for(int i = 1; i < n; i++){
    for(int j = 0; j < 26; j++){
      dp[i][j] = dp[i-1][j];
    }
    dp[i][s[i]-'a']++;//这里用到了前缀和 
  }
  int mi = 1e9;
  for(int i = 0; i < 26; i++){
    int temp = 0, t2 = 0;
    if(dp[n-1][i] < k)  continue;
    while(temp<n && dp[temp][i] == 0) temp++;
    while(t2<n && dp[t2][i] < k) t2++;//这里先找到这个子串含有k个该字母的第一个出现的位置 
    mi = min(mi, t2-temp+1);
    for(temp++; temp < n; temp++){ 
      if(s[temp-1]-'a' == i){//这里是去找剩下子串中满足条件的位置 
        t2++;
        while(t2<n && s[t2]-'a'!=i) t2++;
        if(t2 == n) break;
      }
      mi = min(mi, t2-temp+1);//这里找到满足条件的最小的值 
    }
  }
  if(mi == 1e9) cout<<-1<<endl;
  else cout<<mi<<endl;
  return 0;
} 

下面是今天题目中遇到的前缀和问题:算概率
其实这题考的主要是前缀和问题和概率论里面的如何计算概率,基本上是数学问题,但是还有一个笔者之前不知道的知识盲区:逆元
该题题目为:
在这里插入图片描述这里附上大佬的题解:题解
这题中的逆元体现在怎么去求出不正确的概率,因为在这题中,输入的概率不是普通的概率,而是在mod意义下给出的,所以不正确的概率是mod+1-pi。而普通的不正确概率为1-pi
并且在这题中,还有自己的转移方程,看题解中
在这里插入图片描述
所以最后有这些上面大佬的代码:

#include<iostream>
#include<stdio.h>
using namespace std;
const int N = 2005, mod = 1e9+7;
long long n, p[N], f[N][N];
int main(){
  cin>>n;
  for(int i = 1; i <= n; i++){
    cin>>p[i];
  }
  for(int i = f[0][0] = 1; i <= n; i++){//这里是求前缀和 
    f[i][0] = f[i-1][0]*(mod + 1 - p[i]);//i题里有0题正确的概率,即都错误的概率 
    for(int j = 1; j <= i; j++){
      f[i][j] = f[i-1][j]*(mod+1-p[i]) + f[i-1][j-1]*p[i];
      //i题里面有j题正确的概率=i-1里面已经有了j题正确,第i题要为错误 + i-1题里面只有j-1题正确,所以第i题必须为正确 
    }
  }
  for(int i = 0; i <= n; i++){//最后输出即可 
    cout<<f[n][i]<<" ";
  }
  return 0;
} 

前缀和中找相同的数的问题,可以通过双指针来解决,复杂度更理想:
题目
在这里插入图片描述
这里可以通过双指针去查找相同的数,接下来贴出代码:

#include<iostream>
#include<string>
using namespace std;
int a[100005], b[100005];
int main(){
  int n, m, x, y;
  cin>>n>>m;
  for(int i = 1; i <= n; i++){//求前缀和 
    cin>>x;
    a[i] = a[i-1] + x;
  }
  for(int i = 1; i <= m; i++){
    cin>>y;
    b[i] = b[i-1] + y;
  }
  int i = 1, j, ans = 0;
  int n1 = 1, n2 = 1;
  while(n1 <= n && n2 <= m){//当其中有一个条件不满足时,退出循环 
    if(a[n1] < b[n2])  n1++;//这里去判断是哪个小了,则让哪个下标加一 
    else if(a[n1] > b[n2]) n2++;
    else{//如果相等,则分的段数也加一,且俩个数组的下标也分别往后移动 
      ans++;
      n1++;
      n2++;
    }
  }
  /*for(; i <= n; i++){
    for(j = 1;j <= m; j++){
      if(a[i] == b[j]){
        ans++;
        break;
      }
    }
  }*/
  cout<<ans<<endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值