树状数组
【吐槽】
终于学习新算法了,树状数组好优美呀…好开心,啦啦啦,几天不是很想做题,导致
这五道题目拖了一天,本来打算明天爬泰山的,然而学长突然告诉说明天省赛选
拔,只好作罢(退票)了,但是明天比赛我还是很开(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;
}