POJ 3067 Japan 二维树状数组

76 篇文章 0 订阅
13 篇文章 0 订阅

是一个比较不错的题目。

题目大意是,在一张地图上,西边从上到下均匀排了一列点,东边也是这样,然后给出若干个边,都是从西边的点连到东边的点上。问最后这些边的交点,所谓交点,就是两个边交叉得到的交点,如果交点在结点上,是不算数的。

首先思考一下基本做法,很容易想到,跟某条边相交的边数,跟其在东边和西边的编号有关系。然后就发现,如果某条边在西边的编号大于这条边,并且东边的编号小于这条边,就会出现交叉,当然如果某条边在西边的编号小于这条边,并且东边的编号大于这条边,也会出现交叉。但是,如果两种都计算,显然是会重复计算的,我们就取前一种来计算。

那么就从西边结点,从上到下,对每一条边,观察符合条件的边,然后求和,但是题目给出的边数可能高达一百万。如果直接枚举显然是不靠谱的。

这时,我们再转换一下模型,想象一下以西边顶点为x轴,东边顶点为y轴,建立直角坐标系,那么问题就转化成了,对于每个点,看其右下方点的个数,然后求和,

这时候就不难想到要用到树状数组了。

树状数组要用到二维的,对于每条边呢,用坐标插入时,第一维的x应该是递减的,第二维的y是递增的,表示插入这个坐标后,比其x小,并且y大的在统计的时候都能统计到这个坐标。然后求和的时候就是反过来了。

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 100007
#define INF 1000000000
#define eps 1e-7
using namespace std;
int a[1005][1005];
int xx[1000005], yy[1000005];
int n, m;
int lowbit(int x)
{
    return x & -x;
}
void modify(int x, int y)
{
    for(int i = x; i >= 1; i -= lowbit(i))
        for(int j = y; j <= m; j += lowbit(j))
            a[i][j]++;
}
long long get_sum(int x, int y)
{
    long long sum = 0;
    for(int i = x + 1; i <= n; i += lowbit(i))
        for(int j = y - 1; j >= 1; j -= lowbit(j))
            sum += a[i][j];
    return sum;
}
int main()
{
    int T, k, cas = 0;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= m; j++)
                a[i][j] = 0;
        for(int i = 0; i < k; i++)
        {
            scanf("%d%d", &xx[i], &yy[i]);
            modify(xx[i], yy[i]);
        }
        long long ans = 0;
        for(int i = 0; i < k; i++)
            ans += get_sum(xx[i], yy[i]);
        printf("Test case %d: %I64d\n", ++cas, ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值