hdu6296 代码派对(二维前缀和+差分)

代码派对

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java/Others)
Total Submission(s): 78    Accepted Submission(s): 49


 

Problem Description

比特镇的算法竞赛选手们应邀来到了小Q家中参加盛大的"代码派对"。小Q计划在家中举行一场ACM赛制的联谊赛,3个人为一队进行比赛。

为了让比赛更加有趣,也为了让大家能多多了解他人,小Q在花园的地上画出了一个1000×1000的网格图,从上往下依次编号为第1行到第1000行,从左往右依次编号为第1列到第1000列。小Q让每一名选手选择一个网格图内平行坐标轴的子矩形(x1,y1)−(x2,y2),表示以(x1,y1)和(x2,y2)为对顶点的矩形,包含所有满足x∈[x1,x2]且y∈[y1,y2]的格子。

小Q规定,i,j,k(1≤i<j<k≤n)三人组成一支三人队伍能参赛,当且仅当这三个人选择的子矩形包含至少一个公共格子。请写一个程序,帮助小Q统计有多少种三人组合组成的队伍可以参赛。

第一组样例如下图:


第二组样例如下图:

 

 

 

Input

第一行包含一个正整数T(1≤T≤10),表示测试数据的组数。

每组数据第一行包含一个正整数n(3≤n≤100000),表示选手人数。

接下来n行,每行四个正整数x1,y1,x2,y2(1≤x1≤x2≤1000,1≤y1≤y2≤1000),分别表示每个选手选择的矩形的顶点坐标。

 

 

Output

对于每组数据,输出一行一个整数,即满足条件的三人组合数量。

 

 

Sample Input

 

2 3 3 1 3 1 1 1 2 3 2 1 3 2 5 1 1 4 5 2 1 3 2 2 2 3 3 4 5 4 5 1 2 2 4

 

 

Sample Output

 

0 4

 

 

Source

"字节跳动杯"2018中国大学生程序设计竞赛-女生专场

解题思路

利用差分和二维前缀和求出每个格子上覆盖的区间数g[i][j]。如果这个时候求C(g[i][j],3)的和,那么会产生很多的重复,因为同样的三个区间可以覆盖多个格子。所以我们只保留覆盖区域左上角的那个格子,因为这个格子一定在区间的上边和左边上,所以最后答案为:总和 - 删掉左边后的和 - 删掉上边后的和 + 删掉上边和左边后的和(前面重复减了这一块,所以加回去)。

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 1005
using namespace std;
typedef long long ll;
int a1[maxn][maxn], a2[maxn][maxn], a3[maxn][maxn], a4[maxn][maxn];
ll c3[100005];
ll work(int a[maxn][maxn])
{
    for(int i = 1; i <= 1000; i ++){
        for(int j = 1; j <= 1000; j ++)
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j];
    }
    ll ans = 0;
    for(int i = 1; i <= 1000; i ++){
        for(int j = 1; j <= 1000; j ++){
            ans += c3[a[i][j]];
        }
    }
    return ans;
}
int main()
{
    int T;
    cin >> T;
    c3[3] = 1;
    for(int i = 4; i <= 100000; i ++){
        c3[i] = (ll)i * (i - 1) * (i - 2) / 6;
    }
    while(T --){
        int n;
        cin >> n;
        memset(a1, 0, sizeof(a1));
        memset(a2, 0, sizeof(a2));
        memset(a3, 0, sizeof(a3));
        memset(a4, 0, sizeof(a4));
        for(int i = 1; i <= n; i ++){
            int x1, y1, x2, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            a1[x1][y1] ++, a1[x2 + 1][y2 + 1] ++;
            a1[x1][y2 + 1] --, a1[x2 + 1][y1] --;
            a2[x1 + 1][y1] ++, a2[x2 + 1][y2 + 1] ++;
            a2[x1 + 1][y2 + 1] --, a2[x2 + 1][y1] --;
            a3[x1][y1 + 1] ++, a3[x2 + 1][y2 + 1] ++;
            a3[x1][y2 + 1] --, a3[x2 + 1][y1 + 1] --;
            a4[x1 + 1][y1 + 1] ++, a4[x2 + 1][y2 + 1] ++;
            a4[x1 + 1][y2 + 1] --, a4[x2 + 1][y1 + 1] --;
        }
        cout << work(a1) - work(a2) - work(a3) + work(a4) << endl;
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值