题意:给定一个序列,选择其中的两个数交换位置,问有多少方法使得交换后的逆序数最小。
枚举交换任意两个位置的逆序数。用树状数组优化。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <map>
#include <stack>
#include <string>
#include <algorithm>
#include <vector>
#include <list>
#include <deque>
#define LL long long
#define DB double
#define SI(a) scanf("%d",&a)
#define SD(a) scanf("%lf",&a)
#define SS(a) scanf("%s",a)
#define PF printf
#define MM(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a;i<b;i++)
#define REPD(i,a,b) for(int i=a;i>b;i--)
#define INF 0x3f3f3f3f
#define EPS 1e-8
#define bug puts("bug")
using namespace std;
#define N 5009
int s[N];
inline int low(int k){
return k&(-k);
}
int n;
void add(int a) {
for(int i=a;i<=n;i+=low(i))
s[i]++;
}
int query(int a) {
int ret =0;
for(int i=a;i>0;i-=low(i))
ret += s[i];
return ret;
}
void ini(int n) {
for(int i=0;i<=n;i++) s[i] = 0;
}
int re[N];
int gui() {
int ret = 0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(re[i]>re[j]) ret++;
return ret;
}
void solve() {
int flag = 0;
for(int i=1;i<n;i++)
if(re[i]<re[i-1])
{
flag = 1;
break;
}
if(!flag) {
puts("0 0");
return;
}
int ans1=-1,ans2=0;
for(int i=0;i<n;i++)
{
ini(n);
int ret = 0;
for(int j=i+1;j<n;j++)
{
int a = query(re[i]);
int b = query(re[j]);
// cout<<i<<" "<<j<<" "<<a<<" = "<<b<<endl;
int k = j-i-1;
ret = (2*a-k)+(k-2*b)+(re[i]>re[j]);
add(re[j]);
if(ret>ans1) {
//cout<<"ans1 = "<<ret<<endl;
ans1 = ret;
ans2 = 1;
}else if(ret == ans1) {
ans2++;
//cout<<i<<" "<<j<<endl;
}
}
}
int c = gui();
PF("%d %d\n",c-ans1,ans2);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
SI(n);
for(int i=0;i<n;i++) SI(re[i]),re[i]++;
solve();
return 0;
}