codeforces 853c (关于矩形的主席树)

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 l and 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.

Example
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
Note
The first sample test has one beautiful rectangle that occupies the whole grid, therefore the answer to any query is 1.

In the second sample test the first query rectangle intersects 3 beautiful rectangles, as shown on the picture below:

There are 5 beautiful rectangles that intersect the second query rectangle, as shown on the following picture:

题意

一个n*n的格子,每一行和每一列都有且只有一个黑色格子。“美丽的矩形”定义为一个矩形的四个角中恰好有两个是黑色格子。
对于每次询问,给出一个矩形,查询至少包含给定矩形的一个格子的所有矩形中,“美丽的矩形”的个数。
(不懂的可以看Codeforces853C样例解释)。

解题思路

假设在n*n的区域中给出一个矩形,那么区域会被划分成以下9块,其中第5块表示给定矩形

1 2 3
4 5 6
7 8 9
假定我们用主席树求出了9块中每一块里面包含的黑色格子的个数,第i块内的黑色格子数目记为cnti。
对9块中每一块分别考虑。
对第1块而言,能和它组成“美丽的矩形”并符合要求的黑色格子一定在第5/6/8/9块中。
对第2块而言,能和它组成“美丽的矩形”并符合要求的黑色格子一定在第4/5/6/7/8/9块中。
……
对第5块而言,能和它组成“美丽的矩形”并符合要求的黑色格子一定是所有黑色格子,也就是(n-1)个格子。
……
假设我们把这些答案累加起来,会发现每个“美丽的矩形”被算了两次,因此答案需要/2。

大佬讲得很好啊。。。。对啊。。其实仔细一看 它就是一个序列而已
2e5 要开到 *21

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

int ls[200013*21],rs[200013*21];
int sum[200013*21];
int root[200100];
int tot=0;

void pushup(int now)
{
    sum[now]=sum[ls[now]]+sum[rs[now]];
}


void update(int pre,int &now ,int l,int r,int d)
{
    now=++tot;
    ls[now]=ls[pre];
    rs[now]=rs[pre];
    sum[now]=sum[pre];
    if(l==r&&l==d)
    {
        sum[now]++;
        return ;
    }
    int mid=(l+r)>>1;
    if(d<=mid) update(ls[pre],ls[now],l,mid,d);
    else update(rs[pre],rs[now],mid+1,r,d);
    pushup(now);
}


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

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

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    root[0]=build(1,n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        update(root[i-1],root[i],1,n,x);
    }
    while(q--)
    {
        int l,d,r,u;
        int sum1=0,sum2=0,sum3=0,sum4=0,sum5=0,sum6=0,sum7=0,sum8=0,sum9=0;
        scanf("%d%d%d%d",&l,&d,&r,&u);
        if(d-1>=1)
        {
            sum1=query(root[0],root[l-1],1,d-1,1,n);
            sum2=query(root[l-1],root[r],1,d-1,1,n);
            sum3=query(root[r],root[n],1,d-1,1,n);
        }
        sum4=query(root[0],root[l-1],d,u,1,n);
        sum5=query(root[l-1],root[r],d,u,1,n);
        sum6=query(root[r],root[n],d,u,1,n);

        sum7=query(root[0],root[l-1],u+1,n,1,n);
        sum8=query(root[l-1],root[r],u+1,n,1,n);
        sum9=query(root[r],root[n],u+1,n,1,n);
        // cout<<sum1<<" "<<sum2<<" "<<sum3<<" "<<sum4<<" "<<sum5<<" "<<sum6<<" "
        // <<sum7<<" "<<sum8<<" "<<sum9<<endl;
        ll res=1ll*sum1*(1ll*sum5+sum6+sum8+sum9);
        res+=1ll*sum2*(1ll*sum4+sum5+sum6+sum7+sum8+sum9);
        res+=1ll*sum3*(1ll*sum4+sum5+sum7+sum8);
        res+=1ll*sum4*(1ll*sum2+sum3+sum5+sum6+sum8+sum9);
        res+=1ll*sum5*(n-1);
        res+=1ll*sum6*(1ll*sum2+sum1+sum4+sum5+sum7+sum8);
        res+=1ll*sum7*(1ll*sum2+sum3+sum5+sum6);
        res+=1ll*sum8*(1ll*sum1+sum2+sum3+sum4+sum5+sum6);
        res+=1ll*sum9*(1ll*sum1+sum2+sum4+sum5);
        printf("%lld\n",res/2 );
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值