原题:
E. Tufurama
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
One day Polycarp decided to rewatch his absolute favourite episode of well-known TV series “Tufurama”. He was pretty surprised when he got results only for season 7 episode 3 with his search query of “Watch Tufurama season 3 episode 7 online full hd free”. This got Polycarp confused — what if he decides to rewatch the entire series someday and won’t be able to find the right episodes to watch? Polycarp now wants to count the number of times he will be forced to search for an episode using some different method.
TV series have n seasons (numbered 1 through n), the i-th season has ai episodes (numbered 1 through ai). Polycarp thinks that if for some pair of integers x and y (x < y) exist both season x episode y and season y episode x then one of these search queries will include the wrong results. Help Polycarp to calculate the number of such pairs!
Input
The first line contains one integer n (1 ≤ n ≤ 2·105) — the number of seasons.
The second line contains n integers separated by space a1, a2, …, an (1 ≤ ai ≤ 109) — number of episodes in each season.
Output
Print one integer — the number of pairs x and y (x < y) such that there exist both season x episode y and season y episode x.
Examples
input
5
1 2 3 4 5
output
0
input
3
8 12 7
output
3
input
3
3 2 1
output
2
Note
Possible pairs in the second example:
x = 1, y = 2 (season 1 episode 2 season 2 episode 1);
x = 2, y = 3 (season 2 episode 3 season 3 episode 2);
x = 1, y = 3 (season 1 episode 3 season 3 episode 1).
In the third example:
x = 1, y = 2 (season 1 episode 2 season 2 episode 1);
x = 1, y = 3 (season 1 episode 3 season 3 episode 1).
中文:
给你n个数,第i个数 ai a i 表示存在点(i,1),(i,2)…(i, ai a i ),现在问你有多少个这样的点对(i,j),满足a[i]>=j && a[j]>=i && i
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200001;
ll n;
ll a[maxn],C[maxn];
vector<ll> vi[maxn];
ll lowbit(ll x)
{
return x&(-x);
}
ll sum(ll x)
{
ll ret=0;
while(x>0)
{
ret+=C[x];
x-=lowbit(x);
}
return ret;
}
void add(ll x)
{
while(x<maxn)
{
C[x]++;
x+=lowbit(x);
}
}
void init()
{
memset(C,0,sizeof(C));
for(int i=0;i<maxn;i++)
vi[i].clear();
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n)
{
init();
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=min(a[i],n);
vi[min((ll)i-1,a[i])].push_back((ll)i);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
add(a[i]);
for(int j = 0; j < vi[i].size();j++)
{
ans += sum(n) - sum(vi[i][j]-1);
}
}
cout<<ans<<endl;
}
return 0;
}
思路:
先描述下题目的要求,例如有数据
6
3 2 6 3 2 4
可以表示成如下
1 [1,2,3]
2 [1,2] 存在(2,1)对应第一行的(1,2)
3 [1,2,3,4,5,6] 存在(3,1)对应第一行的(1,3)
4 [1,2,3] 存在(4,3)对应第三行的(3,4)
5 [1,2]
6 [1,2,3,4] 存在(6,3)对应第三行的(3,6)
可以总结出如下规律,考虑数据量的大小,只能使用O(n^2)一下的算法,如果要一个第i个数 ai a i ,那么要找到第i个数之前有多少个满足和 ai a i 相对应的点对,那么一定要满足这样的条件。
1。找下标小于等于 ai a i 的的数据,即下标j且j< ai a i 的数据
2。找 aj a j 大于下标i的数据。
最初的思路比较粗暴,利用可以动态排序的map来记录值 aj a j ,这样会得到由小到大排序的 aj a j ,然后利用二分得到 aj a j 大于下标i的数据的个数。 由于数据只有200000个,可以利用树状数组,来记录下标小于 ai a i 的个数,最后可以在时间复杂度nlogn解决,不过….
map的迭代器是没有办法做减法的,所以没有办法用二分判断 aj a j 大于下标i的数据的个数。
最后问题落到了,如何找到或者修改一种数据结构,来寻找任意一个区间当中大于某个数x的数有多少个?或者手写平衡排序树。
emmmm 貌似有个数据结构叫主席树,可惜不会。
在网上搜索了题解后,想法很巧妙,利用vector[]记录所有数据j< ai a i 满足的值
如上面数据记录的结果是
vi[0]={1}
vi[1]={2}
vi[2]={3,5}
vi[3]={4}
vi[4]={6}
vi[2]={3,5} 表示 a3 a 3 和 a5 a 5 要找下标是2和2之前的数据
由于下标的范围是1到20000,所以数值的范围不会超过这个值,如果超过直接取20000即可。
随后利用树状数组查找每个数据对应的vi[i]当中,出现 ai≥j a i ≥ j 的数据有多少个即可。