E Boredom Codeforce 离线+树状数组

 

Problem

Ilya is sitting in a waiting area of Metropolis airport and is bored of looking at time table that shows again and again that his plane is delayed. So he took out a sheet of paper and decided to solve some problems.

First Ilya has drawn a grid of size n × n and marked n squares on it, such that no two marked squares share the same row or the same column. He calls a rectangle on a grid with sides parallel to grid sides beautiful if exactly two of its corner squares are marked. There are exactly n·(n - 1) / 2 beautiful rectangles.

Ilya has chosen q query rectangles on a grid with sides parallel to grid sides (not necessarily beautiful ones), and for each of those rectangles he wants to find its beauty degree. Beauty degree of a rectangle is the number of beautiful rectangles that share at least one square with the given one.

Now Ilya thinks that he might not have enough time to solve the problem till the departure of his flight. You are given the description of marked cells and the query rectangles, help Ilya find the beauty degree of each of the query rectangles.

Input

The first line of input contains two integers n and q (2 ≤ n ≤ 200 000, 1 ≤ q ≤ 200 000) — the size of the grid and the number of query rectangles.

The second line contains n integers p1, p2, ..., pn, separated by spaces (1 ≤ pi ≤ n, all pi are different), they specify grid squares marked by Ilya: in column i he has marked a square at row pi, rows are numbered from 1 to n, bottom to top, columns are numbered from 1 to n, left to right.

The following q lines describe query rectangles. Each rectangle is described by four integers: l, d, r, u (1 ≤ l ≤ r ≤ n, 1 ≤ d ≤ u ≤ n), here land r are the leftmost and the rightmost columns of the rectangle, d and u the bottommost and the topmost rows of the rectangle.

Output For each query rectangle output its beauty degree on a separate line.

Examples

Input
2 3
1 2
1 1 1 1
1 1 1 2
1 1 2 2
Output
1
1
1
Input
4 2
1 3 2 4
4 1 4 4
1 1 2 3
Output
3
5

题意:n*n的格子坐标中有n个格子被标记,以每两个点为端点可以构成一个举行,每行每列只有1个格子被标记,给q个查询,每个查询给一个矩阵的两个端点(左上角右下角),问与这个矩阵有交集的矩阵的个数

题解:因为每行每列只有一个格子被标记,所以给的那个行数或列数 可以知道这个区域内有多少个点 能组成多少矩阵,比如前3行,有3个点,能组成3*(3-1)/2=3个矩阵。

知道了上面这点,就可以算出初始矩阵有n*(n-1)/2个矩阵(这是显然的,每两个点组成一个矩阵),然后给定一个矩阵,就可以知道他上下左右有多少个矩阵(如果只算一个方向可以O(1)算出来,道理一样),问题是4个角落被重复算了两个,要减掉,然是不知角落里有多少个点,这时候就要一个能"单点修改,区间求和查询(区间就是查询角落有多少点)"的数据结构,树状数组很好的解决了这个问题

E Boredom Codeforce 离线+树状数组_第1张图片

假设f(x)=x*(x-1)/2;//x个点可以组成矩阵的个数

d上方有f(d-1)个矩阵

u下方有f(n-u)个矩阵

l左方有f(l-1)个矩阵

r右方有(n-r)个矩阵

问题是用总个数f(n)减去f(d-1) f(n-u) f(l-1) (n-r)四个角落被减了两次,所以用树状数组维护一个值,就是当前查询Q[i]需要查询的矩阵上方1~k有多少点,那么左上角就有query(l-1)个,右上角有query(n)-query(d)个,是加上query(n)等于d-1。下面两个角反过来在做一遍就可以了。

为了避免重复运算,我们将查询按d排序,这样可以一次加点,前面加的点都是后面需要计算的,所以每个点只会加一次,这样扫一遍可以算出每个查询上面两个角落。反过来按u排序,用同样方法算出下面两个角落。

复杂度O(2mlog(m)+2mlog(n))

#include 
using namespace std;
const int maxn = 3e5 + 2;
typedef long long ll;
struct ques{
	int id, l , r, d, u;
}Q[maxn];
ll ans[maxn];
int p[maxn], last;
bool cmp1(ques A, ques B){
	return A.d < B.d;
}
bool cmp2(ques A, ques B){
	return A.u < B.u;
}
int n ,m;
int tr[maxn];

int lowbit(int x){
	return (x &(-x));
}

void add(int x){
	if (x==0) return ;
	for (int i = x ; i <= n; i+=lowbit(i)){
		tr[i]++;
	}
}

ll query(int x){
	ll ans = 0;
	for (int i = x; i>=1; i-= lowbit(i)){
		ans += tr[i];
	}
	return ans;
}

ll calc(int x){
	return max(0LL ,((ll)x * ((ll)x - 1) /2));
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n;i++){
		int x;
		scanf("%d", &x);
		p[x] = i;
	}
	for (int i = 1; i <= m;i++){
		scanf("%d%d%d%d", &Q[i].l, &Q[i].d, &Q[i].r, &Q[i].u);
		if (Q[i].l > Q[i].r) swap(Q[i].l, Q[i].r);
		if (Q[i].d > Q[i].u) swap(Q[i].d, Q[i].u);
		Q[i].id = i;
		ans[Q[i].id] =(ll)n *((ll)n-1)/2 -calc(Q[i].d-1)-calc(n - Q[i].r)-calc(Q[i].l - 1)-calc(n - Q[i].u );//总个数减上下左右 
	}
	sort(Q+1, Q+1+m, cmp1);
	last = 1;//记录上一次查询的d 
	for (int i = 1; i<= m;i++){
		for (int j = last; j < Q[i].d; j++)	add(p[j]);//将上一个查询到这个查询中间的点加入树状数组中 
		ll num1 = query(Q[i].l-1), num2 = query(n) - query(Q[i].r); //查询1~(l-1)和(n-r)中点的个数 
		ans[Q[i].id] += calc(num1) + calc(num2);//计算两个角落矩阵个数 加到ans中 
		last = Q[i].d; //更新last 
	}
	/*以下是反过来同样操作做一遍*/ 
	memset(tr, 0 ,sizeof(tr));
	sort(Q+1, Q+1+m,cmp2); 
	last = n;
	for (int i = m; i>=1; i--){
		for (int j = last; j > Q[i].u;  j--) add(p[j]);
		ll num1 = query(Q[i].l-1), num2 = query(n) - query(Q[i].r); 
		ans[Q[i].id] += calc(num1) + calc(num2);;
		last = Q[i].u;
	}
	for (int i = 1; i<= m; i++)
		printf("%lld\n", ans[i]);
	return 0;
}

 

你可能感兴趣的:(acm,CodeForces)