Tufurama CodeForces - 961E (树状数组 )

 

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:

  1. x = 1, y = 2 (season 1 episode 2  season 2 episode 1);
  2. x = 2, y = 3 (season 2 episode 3  season 3 episode 2);
  3. x = 1, y = 3 (season 1 episode 3  season 3 episode 1).

In the third example:

  1. x = 1, y = 2 (season 1 episode 2  season 2 episode 1);
  2. x = 1, y = 3 (season 1 episode 3  season 3 episode 1).

 

题意:给出一个n,下面一行 共有n个位置,每个位置上一个值a[i],让你找 共有多少对 x,y 满足下面条件 a[x]>=y&&a[y]>=x&&x<y;n的范围2·105   你要是两层 for 循环的话一定超时, 所以你一定要采取一些 结构来解决这个问题;你想想树状数组,可以知道从 1~n之间的 数的 谁出现过或者最大值、和 等等,1~n的数谁出现过 是最主要的,可以利用插入过后 就立刻查询这个特点 ;  例如数状数组求 逆序数,逆序数 满足 a[x]>a[y]&& y>x; 这个意思就是找y这个位置 之前有几个比自己大的数, 求逆序数,要是给出的n个数 数值比较大,那么就应该先进行离散化,离散化后的序列,除了值变了,但每个位置之间的对应大小关系时不变的, 就让离散化后的这个序列,进行挨个插入添加到数状数组中去,每插入一个就进行查询一次,看看比它小或等于的数,已经插入了几个了,让当前位置 减去 比它小或等于的数 的个数,不就是前面比它大的数的个数吗;

 

看看能不能 从 用树状数组求逆序数中 得出来的经验, 数状数组 中存的是位置; 当前位置有没有出现过,还有就是在树状数组中进行插入时,是按照特定的符合题目要求的序列插入的,看看我们能不能构造出来一个这样的序列呢?

因为它要满足三个条件,看看能不能在插入之前都 已经满足一个条件了?这个构造真的需要自己好好理解理解

因为数状数组求的是 小于等于自己的数已经插入了几个; 如 要是插入的是x,找a[y] 之前出来了几个就行了,满足a[y]>=x这个条件,

那么插入之前能不能 让他们先满足了 a[x] >= y 这个条件呢?请看代码:完美 巧妙的让之满足了这个条件;  

先定一个vector 数组,让v[i][] 中存位置,而当前位置的值为 i ,如i大于n就存到v[n][]中,因为这个位置上的值大于n,而位置范围是1~n,没有大于n位置上的值与之对应,所以就存到v[n][]中就行了;然后枚举 y,从n到1枚举,这样就满足了a[x]>=y这个条件,在利用数树状数组求 前面插入的有几个小于等于a[y]的 数就行了,还有就是注意 y>x 这个条件,代码中那个min(i-1,  a[y]),就是 控制y>x&&找已经插入的 小于等于a[y]的个数 代码中有解释:

代码:


// 树状数组中插入的还是下标;
 
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#include<vector> 
#define Max 2*100000+10
 
int c[Max],a[Max];
vector<int >v[Max];
// v[i][] 中存 给出的数列中数值为i的 位置; 
int n;
 
void init()
{
	int i,j;
	for(i = 0;i<=n;i++)
		v[i].clear();
}
int lowbit(int x)
{
	return x&-x;
}
 
void add(int x,int val)
{
	while(x<=n)
	{
		c[x] += val;
		x += lowbit(x);
	}
}
 
int ssum(int x)
{
	int res = 0;
	while(x>0)
	{
		res +=c[x];
		x -= lowbit(x);
	}
	
	return res;
}
 
int main()
{
	while(~scanf("%d",&n))
	{
		init();
		int i,j;
		
		for(i = 1;i<=n;i++)   // v[][]
 		{
			scanf("%d",&a[i]);
			if(a[i]<=n) v[a[i]].push_back(i);
			else v[n].push_back(i);
		}
		
		memset(c,0,sizeof(c));
		
		long long  res = 0;
		// 枚举y, 
		for(i = n;i>=1;i--)    // 从后往前,v[i][] 中的值为位置,而这个位置上a数组中的值为i,
		 // 设v[i][] 这里面位置为x ,从而a[x]>=i(本来是等于i,而i是从n到1递减的,所以先这样说)
		 //  下面只需找a[i]>=x 的个数就行了,从后往前是为了是为了 找到全部的;
		 // 下面的 min(i-1,a[i]),因为是枚举的y  找的小于y的位置中是不是已经插入过;
		 // 所以要用min(y-1,a[y]);
		{
			for(j = 0;j<v[i].size();j++)   //这一步 是为了让 a[x]>=y;
				add(v[i][j],1);
			res += ssum(min(i-1,a[i])); 
			// 枚举的y,所以要找前面的 i-1是一个限制(为什不是i,i的话就把本身了给算进去了)
			// 还得 满足 a[y]>=x, 所以 又于a[i]比较;  
		}
		printf("%lld\n",res);
	}
	return 0;
} 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值