Contest
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 5;
ll n, res, t[N];
struct Node {
ll a, b, c;
} q[N];
bool cmpa(Node x, Node y) {
return x.a < y.a;
}
bool cmpb(Node x, Node y) {
return x.b < y.b;
}
void add(ll x) {
while (x <= n) {
t[x]++;
x += x & -x;
}
}
ll query(ll x) {
ll sum = 0;
while (x) {
sum += t[x];
x -= x & -x;
}
return sum;
}
int main() {
cin >> n;
for (ll i = 1; i <= n; i++)
cin >> q[i].a >> q[i].b >> q[i].c;
sort(q + 1, q + n + 1, cmpa);
for (ll i = 1; i <= n; i++) {
add(q[i].b);
res += i - query(q[i].b);
}
memset(t, 0, sizeof(t));
for (ll i = 1; i <= n; i++) {
add(q[i].c);
res += i - query(q[i].c);
}
sort(q + 1, q + n + 1, cmpb);
memset(t, 0, sizeof(t));
for (ll i = 1; i <= n; i++) {
add(q[i].c);
res += i - query(q[i].c);
}
cout << res / 2 << '\n';
return 0;
}
题解:三维偏序,CDQ分治法,一维排序,二维归并(分治),三维树状数组(这里是树状数组)
首先一维排好序后,求(二维的逆序对数 + 三维的逆序对数)
然后二维排好序后,求(三维的逆序对数)
最终将结果/2就是答案
A - Japan
#include <float.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define mod 1000000007
const int M = 1e3 + 10;
int t, n, m, k;
int c[M];
struct node {
int u, v;
} e[M * M];
bool cmp(node a, node b) {
if (a.u != b.u)
return a.u < b.u;
return a.v < b.v;
}
int lowbit(int x) {
return x & (-x);
}
void update(int x, int y, int n) {
for (int i = x; i <= n;
i += lowbit(i)) // x为更新的位置,y为更新后的数,n为数组最大值
c[i] += y;
}
int getsum(int x) {
int ans = 0;
for (int i = x; i; i -= lowbit(i))
ans += c[i];
return ans;
}
int main() {
scanf("%d", &t);
for (int j = 1; j <= t; ++j) {
memset(c, 0, sizeof(c));
ll ans = 0;
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < k; ++i) {
scanf("%d%d", &e[i].u, &e[i].v);
}
sort(e, e + k, cmp);
for (int i = 0; i < k; ++i) {
ans += getsum(m) - getsum(e[i].v);
update(e[i].v, 1, m);
}
printf("Test case %d: %lld\n", j, ans);
}
return 0;
}
题解:树状数组的板子题,这里题意是要算线段之间的交点有多少个,因为可能很多,要开longlong。端点分为左端点和右端点,连线只会在左端点和右端点之间连接,这里我用树状数组记录每个右端点所连接的线段数,给线段做个排序,依次从小到大,每次加入一条线段,便算一次交点和,这里我是把比右端点序号还大的其他右端点做一个和(这里的和指的是右端点此时此刻所连接的线段),之后依次加上即可。
B - Ping pong
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define lowbit(x) (x & (x ^ (x - 1)))
#define maxn 100000 + 5
using namespace std;
int n, m;
int c[maxn], a[maxn], b[maxn], d[maxn];
void update(int p) {
while (p <= m) {
c[p]++;
p += lowbit(p);
}
}
int sum(int p) {
int ans = 0;
while (p > 0) {
ans += c[p];
p -= lowbit(p);
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
m = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
m = max(a[i], m);
}
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; i++) {
update(a[i]);
b[i] = sum(a[i] - 1); ///第 i 个人前面比他小的人数,存入b数组中
}
memset(c, 0, sizeof(c));
for (int i = n; i > 0; i--) {
update(a[i]);
d[i] = sum(a[i] - 1); ///第 i 个人后面比他小的人数,存入b数组中
}
long long ans = 0;
for (int i = 2; i < n; i++)
ans += b[i] * (n - d[i] - i) + d[i] * (i - b[i] - 1);
printf("%lld\n", ans);
}
return 0;
}
题解:题意是有不同等级的乒乓球员要打比赛,每场比赛必须有一名裁判,而裁判到他们的任何一个人的距离不应该超过选手之间的距离,而且裁判的水平应该在两名乒乓球员之间。这里枚举了裁判,寻找裁判左边比裁判等级高的和裁判右边比裁判等级低的两两匹配打,也要寻找裁判左边比裁判等级低得和裁判右边比裁判等级高的两两匹配打,再把两者加起来题解链接
珂朵莉的数列
#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define pii pair<int, int>
#define int __int128_t
const int inf = 0x3f3f3f3f;
const int maxn = 1001110;
const int M = 1e9 + 7;
int n, m;
int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + c - '0', c = getchar();
return f * x;
}
void print(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x / 10)
print(x / 10);
putchar(x % 10 + '0');
}
int a[maxn], b[maxn], t[maxn];
int lowbit(int x) //树状数组求前缀和
{
return x & (-x);
}
void update(int i, int x) {
while (i <= m) {
t[i] += x;
i += lowbit(i);
}
}
int query(int x) {
int res = 0;
while (x) {
res += t[x];
x -= lowbit(x);
}
return res;
}
signed main() {
n = read();
for (int i = 1; i <= n; i++) {
a[i] = read();
b[i] = a[i];
}
sort(b + 1, b + 1 + n);
m = unique(b + 1, b + 1 + n) - b - 1; //离散化
int ans = 0;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
int x = m - a[i] + 1; //反过来
ans += (n - i + 1) * query(x - 1);
update(x, i);
}
print(ans);
putchar('\n');
return 0;
}
题解:找规律,看看每个数字的贡献与下标有关题解链接