树状数组

树状数组

【吐槽】

终于学习新算法了,树状数组好优美呀…好开心,啦啦啦,几天不是很想做题,导致

这五道题目拖了一天,本来打算明天爬泰山的,然而学长突然告诉说明天省赛选

拔,只好作罢(退票)了,但是明天比赛我还是很开(shang)心的

POJ 2481

【描述】

给定一系列闭区间,对于每个区间,询问此区间的子区间个数

【分析】

对于一个坐标排序,第二维坐标当做第二关键字逆序排序,然后把y坐标存到c数组

里,因为一个区间的子区间一定出现在该区间之前

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{
int x, y, num;
};
const int N = 100000 + 500;
int n, c[N], tot[N];
node a[N];
void add(int x)
{
    while(x <= 100000)
    {
        c[x]++;
        x += x & -x;
    }
}
int query(int x)
{
    int sum = 0;
    while(x > 0)
    {
        sum += c[x];
        x -= x & -x;
    }
    return sum;
}
bool cmp(node a, node b)
{
    if (a.x == b.x)
         return a.y > b.y;
    return a.x < b.x;
}
int main()
{
    scanf("%d", &n);
    while( n)
    {
        memset(c, 0, sizeof(c));
        for(int i = 0; i < n; i++)
        {
            scanf("%d%d", &a[i].x, &a[i].y);
            a[i].num = i + 1;
        }
        sort(a, a + n, cmp);
        int ans = 1;
        for(int i = 0; i < n; i++)
        {
            tot[a[i].num] = query(100000) - query(a[i].y - 1);
            add(a[i].y);
            if (i > 0 && a[i].x == a[i - 1].x && a[i].y == a[i - 1].y)
            {
               tot[a[i].num] -= ans;
               ans++;
            }else
             ans = 1;
        }
        for(int i = 1; i < n; i++)
            printf("%d ", tot[i]);
        printf("%d\n", tot[n]);
        scanf("%d", &n);
    }
    return 0;
}

POJ 2299

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{
int x, y, num;
};
const int N = 500000 + 500;
int n, c[N], f[N];
node a[N];
void add(int x)
{
    while(x <= n)
    {
        c[x]++;
        x += x & -x;
    }
}
int query(int x)
{
    int sum = 0;
    while(x > 0)
    {
        sum += c[x];
        x -= x & -x;
    }
    return sum;
}
bool cmp(node a, node b)
{
    if (a.x == b.x)
         return a.y > b.y;
    return a.x < b.x;
}
int main()
{
    scanf("%d", &n);
    while( n)
    {
        memset(c, 0, sizeof(c));
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i].x);
            a[i].num = i + 1;
        }
        sort(a, a + n, cmp);
        int ans = 1;
        for(int i = 0; i < n; i++)
        {
            if(i && a[i].x != a[i - 1].x)
                ans++;
            f[a[i].num] = ans;
        }
        long long tot = 0;
        for(int i = 1; i <= n; i++)
        {
            tot += query(n) - query(f[i]);
           // printf("!%d\n", tot);
            add(f[i]);
        }
        cout<<tot<<endl;
        scanf("%d", &n);
    }
    return 0;
}

POJ 1990

【描述】

给定两个序列d[],v[],两个序列均有n个元素,对于数对(x,y),对答案的贡献为

ans = |d[x] - d[y]| * max(v[i], v[j])

询问所有的n*(n)- 1个数对的总贡献

【分析】

第一种方法,注意到max,我们考虑与第x个位置带来的贡献,用树状数组找到比

v[x]小的元素,因为这些元素带来的贡献表达式中max取到的都是v[x],至于前面的

abs,可以先排序d[],然后对于d[x]的前面、后面分别搞一遍树状数组,拿d[]数组

当权值。

第二种方法,排序v数组,树状数组查询一下比d[x]小的元素和、比d[x]大的元素

和,这次应该在树状数组中拿v[]当权值

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 20000 + 500;
typedef long long LL;
struct node{
  int d, v;
};
node a[N];
int c1[N], c2[N], num1[N], num2[N];
int n;
bool cmp(node a, node b)
{
    if (a.d == b.d)
        return a.v < b.v;
    return a.d < b.d;
}
void add(int x, int y, int c[])
{
    while(x <= 20000)
    {
        c[x] += y;
        x += x & -x;
    }
}
LL query(int x, int c[])
{
    LL sum = 0;
    while (x > 0)
    {
        sum += c[x];
        x -= x & -x;
    }
    return sum;
}
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        scanf("%d%d", &a[i].v, &a[i].d);
    sort(a, a + n, cmp);
    long long ans = 0;
    for(int i = 0; i < n; i++)
    {
        ans += (query(a[i].v, num1) * a[i].d - query(a[i].v, c1)) * a[i].v;
        add(a[i].v, a[i].d, c1);
        add(a[i].v, 1, num1);
    }
    for(int i = n - 1; i >= 0; i--)
    {
        ans += (query(a[i].v - 1, c2) - a[i].d *query(a[i].v - 1, num2)) * a[i].v;
        add(a[i].v, a[i].d, c2);
        add(a[i].v, 1, num2);

    }
    cout<<ans;
    return 0;
}

POJ 3416

【分析】

第一感觉是二维的树状数组,然而二维的数组会爆内存,要注意到点比较稀疏(离

散化也没法搞)。正解是离线处理,及把所有的点排序,要求查询的点也要排序,

然后这两个序列的点对应起来,然后跑一遍一维的树状数组就可以啦!!!

sum[i] [ j ]代表矩阵的前缀和, inf_x 代表x的上限 inf _y代表y的上限,对于一次查

询(x,y),答案为:

ans = sum[inf_x][inf_y] - 2 * sum[x][inf_y] - 2 * sum[inf_x][y] + 
      sum[x][y] 

sum[inf_x][inf_y]显然为n,sum[x][inf_y]、sum[inf_x][y]可以扫一遍一维的坐标,

最后这一部分用上面说的方法搞就可以了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N = 500000 + 500;
const int M = 500001;
struct node{
  int x, y, num;
};
node a[N], b[N];
int x[N],  y[N], f[N], tot[N], c[N];
bool cmp(node a, node b)
{
    if (a.x == b.x)
        return a.y < b.y;
    return a.x < b.x;
}
int query(int x)
{
    int sum = 0;
    for(int i = x; i > 0; i -=(i & -i))
        sum += c[i];
    return sum;
}
void add(int x)
{
    for(int i = x; i <= M; i += (i & -i))
        c[i]++;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n, m;
        memset(x, 0, sizeof(x));
        memset(y, 0, sizeof(y));
        memset(c, 0, sizeof(c));

        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
        {
           scanf("%d%d", &a[i].x, &a[i].y);
           a[i].x++;
           a[i].y++;
        }
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d", &b[i].x, &b[i].y);
            b[i].x++;
            b[i].y++;
            b[i].num = i + 1;
        }
        sort(a, a + n, cmp);
        sort(b, b + m, cmp);
        for(int i = 0; i < n; i++)
        {
            x[a[i].x]++;
            y[a[i].y]++;
        }
        for(int i = 1; i <= M; i++)
        {
            x[i] += x[i - 1];
            y[i] += y[i - 1];
        }
        int delta = 0;
        printf("just for test\n");
        for(int i = 0; i < n; i++)
        {
          //  printf("ha%d\n", i);
                while(b[delta].x < a[i].x && delta <= m)
            {
                f[delta] = query(b[delta].y - 1);
                delta++;
            }
            add(a[i].y);
        }
      //  printf("!just for test\n");
        for(int i = 0; i < m; i++)
        {
            tot[b[i].num] = n - 2 * x[b[i].x] - 2 * y[b[i].y] + 4 * f[i];
            tot[b[i].num] = abs(tot[b[i].num]);
        }
        for(int i = 1; i <= m; i++)
            printf("%d\n", tot[i]);
        printf("\n");
    }
    return 0;
}

POJ 3067

【分析】

裸裸的逆序对,注意报int

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N = 1000000 + 500;
struct node{
  int x, y, num;
};
node a[N];
int  c[N];
int n, m, k, Case = 0;
bool cmp(node a, node b)
{
    if (a.x == b.x)
        return a.y < b.y;
    return a.x < b.x;
}
int query(int x)
{
    int sum = 0;
    for(int i = x; i > 0; i -=(i & -i))
        sum += c[i];
    return sum;
}
void add(int x)
{
    for(int i = x; i <= m; i += (i & -i))
        c[i]++;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
       scanf("%d%d%d", &n, &m, &k);
       for(int i = 0; i < k; i++)
        scanf("%d%d", &a[i].x, &a[i].y);
       sort(a , a + k, cmp);
       memset(c, 0, sizeof(c));
       long long  ans = 0;
       for(int i = k - 1; i >= 0; i--)
       {
           ans += query(a[i].y - 1);
          // printf("this is just for test %d\n", ans);
           add(a[i].y);
       }
       cout<<"Test case "<<++Case<<": "<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值