CF961E Tufurama

题目描述

有一天Polycarp决定重看他最喜爱的电视剧《Tufurama》。当他搜索“在线全高清免费观看Tufurama第3季第7集”却只得到第7季第3集的结果时,他很惊讶。这让Polycarp感到疑惑——如果有天他决定重看整个系列却无法找到正确的剧集观看,那该怎么办呢?Polycarp现在想统计一下他被迫用不同方案搜索同一剧集的次数。

电视连续剧有n 季(从1 到n 编号),第i 季有ai 集(从1 到ai 编号)。Polycarp认为如果有一对x 和y (x<y),使第x 季第y 集、第y 季第x 集存在,那么其中一个搜索就会包含错误的内容。请帮助Polycarp统计这样的数对的数量吧!

输入输出格式

输入格式

第一行,一个整数n(1n210^5) ,表示季数。

第二行,n 个用空格隔开的整数a1,a2,...,an(1ai10^9) ,表示每一季的集数。

输出格式

只有一行,一个整数,表示x 和y (x<y ),使第x季第y集、第y季第x集存在的数对的数量。

输入输出样例

输入样例#1: 复制
5
1 2 3 4 5
输出样例#1: 复制
0
输入样例#2: 复制
3
8 12 7
输出样例#2: 复制
3
输入样例#3: 复制
3
3 2 1
输出样例#3: 复制
2

说明

在样例2中可能的对数:

  1. x=1,y=2 (第1季第2集第2季第1集)
  2. x=2,y=3 (第2季第3集第3季第2集)
  3. x=1,y=3 (第1季第3集第3季第1集)

在样例3中:

  1. x=1,y=2 (第1季第2集第2季第1集)
  2. x=1,y=3 (第1季第3集第3季第1集)

每到一季相当于我要寻找集数等于季数的电视 我们规定寻找的数对是保证集数大于季数的 这样子不会找重复

 

具体做法是 每到新的一季 就在前面季数里查找有没有集数等于当前季数的 比如这张图 我们就需要查找前面高于这条线的数量

但是这样子是不严谨的 因为我们现在只保证了前面的集数等于现在的季数 但是没有保证现在的集数等于之前的季数 

比如第五季有三集 但是直接查询前面会查询到第四季的第五集 无法满足 所以要保证这个问题 对于第$i$季

我们就只需要查询$min(i - 1, a[i])$到起点有多少大于$i$季数的即可 这个可以使用主席树维护 

要寻找高于标准的数量使用差分即可 这道题不用离散化 因为在满足条件的数对当中最大的元素肯定是小于等于季数的 可以直接修改 要是超界就在$n + 1$修改即可

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 5 * 1e5 + 5;
int n, a[N], sum[32 * N], ls[32 * N], rs[32 * N], cnt, root[N];
long long ans;

void update(int nd) {
    
    sum[nd] = sum[ls[nd]] + sum[rs[nd]];
}

int modify(int pre, int l, int r, int pos, int del) {
    
    int nd = ++ cnt;
    sum[nd] = sum[pre], ls[nd] = ls[pre], rs[nd] = rs[pre];
    if(l == r) {
        sum[nd] += del; return nd;
    }
    int mid = l + r >> 1;
    if(pos <= mid) ls[nd] = modify(ls[pre], l, mid, pos, del);
    else rs[nd] = modify(rs[pre], mid + 1, r, pos, del);
    update(nd); return nd;
}

int query(int nd, int l, int r, int L, int R) {
    
    if(l >= L && r <= R) return sum[nd];
    int mid = l + r >> 1;
    int ans = 0;
    if(L <= mid) ans += query(ls[nd], l, mid, L, R);
    if(mid < R)  ans += query(rs[nd], mid + 1, r, L, R);
    return ans;
}

int build(int l, int r) {
    
    int nd = ++ cnt;
    if(l == r) {
        sum[nd] = 0; return nd;
    }
    int mid = l + r >> 1;
    ls[nd] = build(l, mid); rs[nd] = build(mid + 1, r);
    return nd;
}

void Solve( ) {
    
    root[0] = build(1, n);
    for(int i = 1;i <= n;i ++) {
        root[i] = modify(root[i - 1], 1, n + 1, 1, 1);
        root[i] = modify(root[i], 1, n + 1, min(n + 1, a[i] + 1), -1);
        int l = min(a[i], i - 1);
        ans += 1ll * query(root[l], 1, n + 1, 1, i);
    }
    printf("%lld\n", ans);
}

int main( ) {
    
    scanf("%d",& n);
    for(int i = 1;i <= n;i ++) scanf("%d",& a[i]);
    Solve( );
}
posted @ 2018-10-23 11:51  阿澈说他也想好好学习  阅读(691)  评论(0编辑  收藏  举报