Educational Codeforces Round 94 (Rated for Div. 2) D题 一维前缀和的另一种用法

博客介绍了如何解决一个关于区间子序列计数的问题,原始方法可能导致O(n^3)的时间复杂度,导致TLE。通过优化,利用前缀和技巧将算法改进为O(n^2),成功避免了超时并给出了解决方案的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://codeforces.com/contest/1400/problem/D

  • 题目很短,意思很清楚,我的想法是记录每一个具有相同元素的区间,记录元素及其位置,然后暴力两两枚举二分位置计算,但这样时间复杂度可能会达到 O ( n 3 ) O(n^3) O(n3),写成下面这样会 T L E   o n   t e s t   9 TLE\ on \ test\ 9 TLE on test 9
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  while(t--){
    int n;
    cin >> n;
    map<int, vector<int> > mp;
    vector<int> a(n);
    for(int i=0;i<n;i++){
      cin >> a[i];
      mp[a[i]].push_back(i);
    }
    ll ans = 0;
    for(int i=0;i<n;i++){
      for(int k=i+1;k<n;k++){
        if(a[i] != a[k]) continue;
        for(auto p : mp){
          if(p.first == a[i]){
            int j = upper_bound(p.second.begin(), p.second.end(), i) - p.second.begin();
            int l = upper_bound(p.second.begin(), p.second.end(), k) - p.second.begin();
            int numl = l - 1 - j;
            int numr = (p.second.size() - l);
            if(numl >= 0 && numr >= 0) ans += 1ll * numl * numr;
          }else{
            int j = upper_bound(p.second.begin(), p.second.end(), i) - p.second.begin();
            int l = upper_bound(p.second.begin(), p.second.end(), k) - p.second.begin();
            int numl = l - j;
            int numr = (p.second.size() - l);
            if(numl >= 0 && numr >= 0) ans += 1ll * numl * numr;
          }
        }
      }
    }
    cout << ans << '\n';
  }
  return 0;
}
  • 但是考虑到这个题的 a [ i ] a[i] a[i]很小,所以我们可以设 s u m [ i ] [ j ] sum[i][j] sum[i][j]表示前 j j j个数有多少个 i i i,然后枚举每一个可能的位置 j j j k k k,枚举这两个点可以确定剩下的 i i i l l l,如果换一种枚举办法就不太容易,所以我们可以在 O ( n 2 ) O(n^2) O(n2)的时间复杂度内得到答案
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  while(t--){
    int n;
    cin >> n;
    vector<int> a(n + 1);
    vector<vector<int> > sum(n + 1, vector<int>(n + 1));
    for(int i=1;i<=n;i++){
      cin >> a[i];
    }
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        sum[i][j] = sum[i][j - 1] + (i == a[j]);
      }
    }
    ll ans = 0;
    for(int j=1;j<=n;j++){
      for(int k=j+1;k<=n;k++){
        ans += 1ll * sum[a[k]][j - 1] * (sum[a[j]][n] - sum[a[j]][k]);
        // a[k]和前j-1个数匹配; a[j]和前k个数匹配
      }
    }
    cout << ans << '\n';
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Clarence Liu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值