原题链接:https://codeforces.com/contest/1676/problem/H1 https://codeforces.com/contest/1676/problem/H2
题目背景:
有上下条线,每条线等分为 n 段,给出长度为 n 的数组 a , a[i] 表示有一直线从上线i处连接到下线 a[i] 处,请问最大交叉数量为多少。
思路:
观察图中给出左边线可发现我们只需找出位置比自己靠后且值小于自己的点即可,观察右边线又可以发现不仅值小于自己的点可以,值相等也可以,综上我们只需找出位置比 i 靠后且值小于等于 a[i] 的点的数量即可。
暴力:通过以上的思路我们可以使用双重循环即可。
优化:使用树状数组动态维护在 i 点前且值比 a[i] 大的点的数量。
数据范围:
t <= 1000,n 总和小于2e5。
时间复杂度:
暴力: O(n^2)
树状数组:O(nlogn)
ac代码:
#include <bits/stdc++.h>
#define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> pii;
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, 1, 0, -1};
const int MAX = (1ll << 31) - 1;
const int MIN = 1 << 31;
const int N = 2e5 + 10;
int n;
int a[N];
ll t[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i))
{
t[i] += v;
}
}
ll query(int x)
{
ll ans = 0;
for (int i = x; i; i -= lowbit(i))
{
ans += t[i];
}
return ans;
}
void solve()
{
memset(a, 0, sizeof a);
memset(t, 0, sizeof t);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
ll ans = 0;
for (int i = 1; i <= n; i++)
{
ans += query(n) - query(a[i] - 1);
add(a[i], 1);
}
cout << ans << endl;
}
int main()
{
ioscc;
int T;
cin >> T;
while (T--)
solve();
return 0;
}