• [CSP-S模拟测试]:Rectangle(模拟+树状数组)


    题目描述

    平面上有$n$个点,第$i$个点的坐标为$X_i,Y_i$。对于其中的一个非空点集$S$,定义$f(S)$为一个最小矩形,满足:
    $ullet$覆盖$S$中所有的点(在边界上也算覆盖);
    $ullet$边与坐标轴平行。
    求所有不同的$f(S)$的面积和对$10^9+7$取模的结果。两个矩形被认为是不同的,当且仅当它们顶点坐标不同。


    输入格式

    从文件$rectangle.in$中读入数据。
    第一行一个整数$n$。
    接下来$n$行,每行两个整数$X_i,Y_i$。


    输出格式

    输出到文件$rectangle.out$中。
    一行一个整数表示答案。


    样例

    样例输入:

    4
    1 2
    3 1
    4 4
    5 1

    样例输出:

    45


    数据范围与提示

    样例解释:

    有$8$个面积大于$0$的不同矩形,以下是它们左下角和右上角的坐标:
    $(1,1),(3,2);(1,1),(4,4);(1,1),(5,2);(1,1),(5,4)$
    $(1,2),(4,4);(3,1),(4,4);(3,1),(5,4);(4,1),(5,4)$

    数据范围:

    对于所有数据,满足$2leqslant nleqslant 10^4,1leqslant X_i,Y_ileqslant 2500$,没有重复的点。
    $ullet Subtask1(13\%)$,$nleqslant 18$。
    $ullet Subtask2(9\%)$,$nleqslant 50$。
    $ullet Subtask3(25\%)$,$nleqslant 300$。
    $ullet Subtask4(21\%)$,$nleqslant 2500,X_i eq X_j,Y_i eq Y_j$。
    $ullet Subtask5(19\%)$,$nleqslant 2500$。
    $ullet Subtask6(13\%)$,没有特殊的约束。


    题解

    先来考虑$21\%$的$X_i eq X_j,Y_i eq Y_j$的情况。

    我们可以$n^2$枚举左右边界,那么设边界上的点为$(L,y_1)$和$(R,y_2)$。

    那么只有位于$(L,R)$且纵坐标$>max(y_1,y_2)$和$<min(y_1,y_2)$的点才能做贡献,我们可以考虑树状数组,存储$sum y$即可(长度是变化的,但是高度不变)。

    现在来考虑一般情况,每个$L$和$R$上可能有很多的点,我们依次枚举计数即可。

    但是可能会出现如下图中的情况:

    显然,我们在统计答案点$1,3$和点$2,3$的贡献的时候会将紫色矩阵算重,不用担心,我们只需要将纵坐标最靠下的统计就好了。

    代码实现稍繁琐。

    时间复杂度:$Theta(nmlog m)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1000000007;
    int n;
    int Map[2501][2501];
    int tr[2][2501][2501];
    bool vis[2501][2501];
    long long ans;
    int lowbit(int x){return x&-x;}
    void add(int id,int k,int x,int w)
    {
    	for(int i=x;i<=2500;i+=lowbit(i))
    		tr[id][k][i]+=w;
    }
    int ask(int id,int k,int x)
    {
    	int res=0;
    	for(int i=x;i;i-=lowbit(i))res+=tr[id][k][i];
    	return res;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		Map[x][++Map[x][0]]=y;
    	}
    	for(int i=1;i<=2500;i++)
    	{
    		sort(Map[i]+1,Map[i]+Map[i][0]+1);
    		Map[i][Map[i][0]+1]=2501;
    	}
    	for(int i=1;i<=2500;i++)
    	{
    		if(!Map[i][0])continue;
    		for(int j=1;j<=Map[i][0];j++)
    			if(!vis[i][Map[i][j]])
    			{
    				vis[i][Map[i][j]]=1;
    				add(1,i,Map[i][j],1);
    				add(0,i,Map[i][j],Map[i][j]);
    			}
    		for(int j=i-1;j;j--)
    		{
    			if(!Map[j][0])continue;
    			int l1=1,l2=1;
    			for(int k=1;k<=Map[j][0];k++)
    				if(!vis[i][Map[j][k]])
    				{
    					vis[i][Map[j][k]]=1;
    					add(1,i,Map[j][k],1);
    					add(0,i,Map[j][k],Map[j][k]);
    				}
    			int wzc=max(Map[i][1],Map[j][1]);
    			while(Map[i][l1+1]<=wzc)l1++;
    			while(Map[j][l2+1]<=wzc)l2++;
    			while(l1<=Map[i][0]&&l2<=Map[j][0])
    			{
    				int flag=min(Map[i][l1+1],Map[j][l2+1]);
    				ans=(ans+1LL*(i-j)*((ask(0,i,flag-1)-ask(0,i,wzc-1))*ask(1,i,min(Map[i][l1],Map[j][l2]))-((ask(1,i,flag-1)-ask(1,i,wzc-1))*ask(0,i,min(Map[i][l1],Map[j][l2])))))%mod;
    				wzc=flag;
    				if(Map[i][l1+1]<=wzc)l1++;
    				if(Map[j][l2+1]<=wzc)l2++;
    			}
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误
    HTTP KeepAlive模式
    Windows 7 中的 God Mode
    我的开发环境配置经验
    C#格式化数值结果表(格式化字符串)
    我可怜的笔记本电脑
    JetBrains ReSharper 5.x 注册机
    异常处理准则
    调用 Windows 7 中英文混合朗读
    oracle笔记(2010130)
  • 原文地址:https://www.cnblogs.com/wzc521/p/11619495.html
Copyright © 2020-2023  润新知