A. Ultra-QuickSort
题意:
交换相邻元素使n个元素的序列成为升序
Sol:
求逆序对的数量
- 逆序对(归并排序,树状数组)
Code:
- 归并排序
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int n;
int a[N], tmp[N];
LL res; // 答案可能会超过int
void merge_sort(int a[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(a, l, mid);
merge_sort(a, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (a[i] <= a[j]) tmp[k ++ ] = a[i ++ ];
else {
res += mid - i + 1;// l-mid为升序,故逆序对贡献就是这之间所有的数
tmp[k ++ ] = a[j ++ ];
}
while (i <= mid) tmp[k ++ ] = a[i ++ ];
while (j <= r) tmp[k ++ ] = a[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) a[i] = tmp[j];
}
int main(){
while(scanf("%d", &n), n)
{
res = 0;
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
merge_sort(a, 1, n);
printf("%lld\n", res);
}
return 0;
}
2.树状数组
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 5e5+10;
int n, m, a[N], b[N];
int tr[N];
int find(int x)
{
int l = 1, r = m;
while( l < r)
{
int mid = l + r >> 1;
if(b[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
// 树状数组的基本操作
void add(int x, int c)
{
for(int i = x; i <= m; i += (i & -i)) tr[i] += c;
}
int sum(int x)
{
int res = 0;
for(int i = x; i ; i -= (i & -i)) res += tr[i];
return res;
}
void solve()
{
memset(tr, 0, sizeof tr);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]), b[i] = a[i];
//利用b数组对a数组进行离散化(排序去重)
sort(b + 1, b + 1 + n);
m = 1;
for (int i = 2; i <= n; i ++ )
if(b[i] != b[m] ) b[++m] = b[i];
LL res = 0;
// 通过a数组的值找打其在b数组的位置
for (int i = 1; i <= n; i ++ )
{
int x = find(a[i]);
res += sum(m) - sum(x); //前面大于这个数的数量
add(x, 1); // 将x这个数插入树状数组
}
printf("%lld\n", res);
}
int main()
{
while(scanf("%d", &n), n) solve();
}
B.敌兵布阵
题意:
有
n
n
n个营地,第
i
i
i个营地有
a
i
a_i
ai个人,接下来有若干条命令,
- (1) Add
i
i
i
j
j
j,
i
i
i和
j
j
j为正整数,表示第
i
i
i个营地增加
j
j
j个人(
j
j
j不超过30)
- (2) Sub
i
i
i
j
j
j ,
i
i
i和
j
j
j为正整数,表示第
i
i
i个营地减少
j
j
j个人(
j
j
j不超过30);
- (3) Query
i
i
i
j
j
j ,
i
i
i和
j
j
j为正整数,
i
≤
j
i \le j
i≤j,表示询问第
i
i
i到第
j
j
j个营地的总人数;
- (4) End 表示结束,这条命令在每组数据最后出现;
Sol:
数组数组的基本操作:单点修改和区间和查询
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 500010;
int n, Cas;
int a[N];
int tr[N];
int lowbit(int x) { return x & -x; }
void add(int x, int c)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum (int x)
{
int res = 0;
for(int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
void solve()
{
memset(tr, 0, sizeof tr);
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
add(i, a[i]);
}
char op[10];
bool ok = false;
while(1)
{
scanf("%s", op);
if(*op == 'Q')
{
int l, r;
scanf("%d%d", &l, &r);
if(!ok) printf("Case %d:\n", ++Cas), ok = true;
printf("%d\n", sum(r) - sum(l - 1));
}
else if(*op == 'A')
{
int l, r;
scanf("%d%d", &l, &r);
add(l, r);
}
else if(*op == 'S')
{
int l, r;
scanf("%d%d", &l, &r);
add(l, -r);
}
else { break; }
}
}
int main()
{
int T = 1;
scanf("%d", &T);
while( T -- )
solve();
return (0-0);
}
C.Color the ball
题意:
有
n
n
n个气球,编号为
1
∼
n
1 \sim n
1∼n,然后
n
n
n次染色, 读入
a
a
a和
b
b
b表示
a
a
a到
b
b
b染色一次。输出每个气球染色的次数
Sol:
数组数组维护差分序列
- 差分序列
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m, a[N], tr[N];
int lowbit(int x) { return x & -x; }
void add(int x, int c)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x)
{
int res = 0;
for(int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
int main(){
while(scanf("%d", &n), n)
{
memset(tr, 0, sizeof tr);
for(int i = 1; i <= n; ++i)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, 1);
add(b + 1, -1);
}
for(int i = 1; i <= n; ++i)
if(i > 1) printf(" %d", sum(i));
else printf("%d", sum(i));
puts("");
}
return 0;
}
D.Japan
题意:
有
n
n
n,
m
m
m座岛中间相隔一条河流,之间有
k
k
k条桥,问这
k
k
k条桥有多少个交点
Sol:
对
k
k
k座桥,按照右端点升序。如果左端点出现逆序,将会存在交点。类似求逆序对的作贡献
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5e5 + 10;
int n, m, k;
struct node{
int x, y;
bool operator < (const node& b) const{
if(y == b.y) return x < b.x;
return y < b.y;
}
}a[N + 10];
LL tr[N + 10];
int lowbit(int x) { return x & -x; };
void add(int x, LL c)
{
for(int i = x; i <= N; i += lowbit(i)) tr[i] += c;
}
LL sum(LL x)
{
LL res = 0;
for(int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
int main(){
int T;
int Cas = 1;
read(T);
while(T -- )
{
memset(tr, 0, sizeof tr);
read(n); read(m); read(k);
for(int i = 1; i <= k; ++i)
{
read(a[i].x);read(a[i].y);
}
sort(a+1, a+1+k);
LL ans = 0;
for(int i = 1; i <= k; ++i)
{
ans += sum(N) - sum(a[i].x);
add(a[i].x, 1LL);
}
printf("Test case %d: %lld\n", Cas ++, ans);
}
return 0;
}
E.Matrix
题意:
有一个
n
∗
n
n*n
n∗n的矩阵,一开始都是0。有两个操作:
- 1.C x 1 x_1 x1 y 1 y_1 y1 x 2 x_2 x2 y 2 y_2 y2 将左上角 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)和右下角 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)矩阵内元素反转。
- 2.Q x x x y y y 查询 ( x , y ) (x,y) (x,y)处的值
Sol:
二维树状数组。记录每个元素的反转次数。
Code:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
const int N = 1010;
int tr[N][N], t, n, q;
int lowbit(int x) { return x & -x; };
void add(int x, int y, int c)
{
for(int i = x; i <= N; i += lowbit(i))
for(int j = y; j <= N; j += lowbit(j))
tr[i][j] += c;
}
int sum(int x, int y)
{
int res = 0;
for(int i = x; i > 0 ; i -= lowbit(i))
for(int j = y; j > 0 ; j -= lowbit(j))
res += tr[i][j];
return res;
}
int main(){
scanf("%d", &t);
while(t--)
{
memset(tr, 0, sizeof tr);
scanf("%d%d", &n, &q);
while(q--)
{
char op[10];
int x, y, x1, y1;
scanf("%s", op);
if(*op == 'C') {
scanf("%d%d%d%d", &x, &y, &x1, &y1);
add(x, y, 1);
add(x1 + 1, y1 + 1, 1);
add(x, y1 + 1, -1);
add(x1+1, y, -1);
}
else {
scanf("%d%d", &x, &y);
printf("%d\n", sum(x, y) & 1);
}
}
if(t) printf("\n");
}
return 0;
}