Codeforces Round #825 (Div. 2)C1. Good Subarrays (Easy Version)

79 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
本文已参与「新人创作礼」活动,一 起开启掘金创作之路。

题目

image.png 中文翻译

image.png 中文大意

就是让我们在数组a种选取一段连续的区间作为数组b数组b的下标从一开始并且b[i]>ib[i] > i问一共有多少种方法

双指针解法

我们会发现如果lrl-r这段区间的数字符合题意那么(l+1)r(l+1)-r这段区间也是一定符合题意的所有我们只需要维护有边界r即可

int a[N];
void solve()
{
   int res = 0;
   int n; cin >> n;
   rep(i,n) cin >> a[i];
   int l = 1,r = 1;
   int cnt = 1;
   for(l; l <= n; l++) {
      r = max(l,r);
      while(r + 1 <= n && a[r + 1] >= cnt + 1) {
        r ++;
        cnt ++;
      }
      cnt--;
      cnt = max(1ll,cnt);
      // cout << r - l + 1 << endl;
      int len = r - l + 1;
      res += len;
   }
   cout << res << endl;

}

线段树写法

我们发现如果以ll为区间的左端点那么a[r]>=rl+1a[r] >= r-l + 1这样我们可以发现符合的区间一定是连续的所以我们可以采用线段树二分来写这题

const int N = 2e5 + 10;
int a[N];
int f[N << 2];
int Max[N << 2], Min[N << 2];
void Built(int k,int l,int r) {
   Max[k] = 0ll;
   Min[k] = 1e18;
   if( l == r) {
      f[k] = a[l];
      Max[k] =  Min[k] = a[l];
      return;
   }
   int m = l + r >> 1;
   Built(k << 1,l,m);
   Built(k << 1|1,m + 1,r);
   Max[k] = max(Max[k << 1],Max[k << 1|1]);
   Min[k] = min(Min[k << 1],Min[k << 1|1]);
}
int Binary_search(int k,int l,int r,int x) {
     if(r <= x)  return r;
     if(x < Min[k]) return -1;
     if(x >= Max[k]) return r;
     int m = l + r >> 1;
     int res = Binary_search(k << 1,l,m,x);
     if(res == m) {
         return max(res,Binary_search(k << 1|1,m + 1,r,x));
     }else return res;
}
void solve() {
    int n;cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[i] = i  - a[i] + 1;
    }
    // rep(i,n) cout << a[i] << " \n"[ i == n];
    a[0] = 1e9;
    Built(1,1,n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int R = Binary_search(1,1,n,i);
        // cout << "----" << R << endl;
        ans += R - i + 1;
    }
    cout << ans << endl; 
}