>Description
>Input
>Output
>Sample Input
4
1 4 3 2
>Sample Output
3
>解题思路
首先我们要知道一个树状数组与逆序对的东西(所以为了防爆,得用离散化把S序列离散一下在加进树状数组)
然后我们把符合(a,b)的方案数p,符合(c,d)的方案数q求出来(树状数组求逆序对,具体得理解树状数组太多了不想打了 ),ans=pq,
接着处理a≠b≠c≠d的情况,因为a已经小于b,c已经小于d,所以就只用在ans中减去a=c、a=d、b=c、b=d的情况,
预先求出lb[i],rb[i],ls[i],rs[i](分别为d为i时c的方案,a为i时b的方案,b为i时a的方案,c为i时d的方案)
四种情况(冒号后面a<b<c,分别考虑四种重叠情况下三个数的状态):
- a=c:s[a]>s[b],s[a]<s[c] 方案数为rb[i]*rs[i]
- a=d:s[a]>s[b]<s[c] 方案数为lb[i]*rb[i]
- b=c:s[a]<s[b]>s[c] 方案数为ls[i]*rs[i]
- b=d:s[a]>s[c],s[b]<s[c] 方案数为ls[i]*lb[i]
(自己推推就很容易理解了)
最后在加个longlong,不然只有20分
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
struct ooo
{
ll c, h;
} ss[100005];
ll n, t, s[100005], f[200005], p, q, ans;
ll lb[100005], rb[100005], ls[100005], rs[100005];
bool bmp (ooo aa, ooo bb) {return aa.c < bb.c;}
void add (ll x, ll y)
{
for (; x <= n; x += x & (-x))
f[x] += y;
} //树状数组改值
ll ask (ll x)
{
ll l = 0;
for (; x; x -= x & (-x))
l += f[x];
return l;
} //树状数组询问
int main()
{
scanf ("%lld", &n);
for (ll i = 1; i <= n; i++)
scanf ("%lld", &ss[i].c), ss[i].h = i;
sort (ss + 1, ss + 1 + n, bmp);
s[ss[1].h] = t = 1;
for (ll i = 2; i <= n; i++)
if (ss[i].c != ss[i - 1].c) s[ss[i].h] = ++t;
else s[ss[i].h] = t; //离散化
for (ll i = 1; i <= n; i++)
{
add (s[i], 1);
ls[i] = ask (s[i] - 1);
lb[i] = ask (n) - ask (s[i]);
p += ls[i];
q += lb[i];
}
ans = p * q;
memset (f, 0, sizeof (f));
for (ll i = n; i >= 1; i--)
{
add (s[i], 1);
rs[i] = ask (s[i] - 1);
rb[i] = ask (n) - ask (s[i]);
}
for (ll i = 1; i <= n; i++)
ans -= rs[i] * rb[i] + lb[i] * rb[i]
+ ls[i] * rs[i] + lb[i] * ls[i];
printf ("%lld", ans);
return 0;
}