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(1≤n≤2⋅10^5) ,表示季数。
第二行,n 个用空格隔开的整数a1,a2,...,an(1≤ai≤10^9) ,表示每一季的集数。
输出格式
只有一行,一个整数,表示x 和y (x<y ),使第x季第y集、第y季第x集存在的数对的数量。
输入输出样例
说明
在样例2中可能的对数:
- x=1,y=2 (第1季第2集第2季第1集)
- x=2,y=3 (第2季第3集第3季第2集)
- x=1,y=3 (第1季第3集第3季第1集)
在样例3中:
- x=1,y=2 (第1季第2集第2季第1集)
- 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( ); }