题解 Time

传送门

首先枚举最大值,两边分别求逆序对的做法是错误的,这里是来自战神的hack数据

1 2 100 99 98 3 97 96 95 94 93 92 91

显然3应该跨过最大值到左边去,所以这个做法就没有正确性了

然后正解:
发现最小值一定会被移到最边上,而且因为它最小,就不会再有数跨过它移动
所以这东西没有后效性,且移动后会形成子问题
所以不断取最小值,贪心地取次数最小的方向移动,然后把它删了就行了

  • 所以……要交换序列里的元素,使它们成某种顺序的时候,可以考虑「什么元素不可被跨越」或者「什么元素一定会被移动到什么位置」,然后设法固定这些元素,其余的转化为子问题?

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
#define reg register int
#define fir first 
#define sec second 
#define make make_pair
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int x[N];

namespace task1{
	int uni[N], usize, lim;
	int ans=INF, a[N];
	inline void upd(int i) {for (; i<=lim; i+=i&-i) ++a[i];}
	inline int query(int i) {int ans=0; for (; i; i-=i&-i) ans+=a[i]; return ans;}
	void solve() {
		for (int i=1; i<=n; ++i) uni[i]=x[i];
		sort(uni+1, uni+n+1);
		usize=unique(uni+1, uni+n+1)-uni-1; lim=usize+2;
		for (int i=1; i<=n; ++i) x[i]=lower_bound(uni+1, uni+usize+1, x[i])-uni+1;
		for (int i=1,cnt; i<=n; ++i) {
			memset(a+1, 0, sizeof(int)*lim);
			cnt=0;
			for (int j=i; j; --j) {
				cnt+=query(x[j]-1);
				upd(x[j]);
			}
			memset(a+1, 0, sizeof(int)*lim);
			for (int j=i+1; j<=n; ++j) {
				cnt+=query(x[j]-1);
				upd(x[j]);
			}
			ans=min(ans, cnt);
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	int a[N]; ll ans;
	pair<int, int> s[N];
	inline void upd(int i) {for (; i<=n; i+=i&-i) ++a[i];}
	inline int query(int i) {int ans=0; for (; i; i-=i&-i) ans+=a[i]; return ans;}
	inline void del(int i) {for (; i<=n; i+=i&-i) --a[i];}
	void solve() {
		for (int i=1; i<=n; ++i) upd(i);
		for (int i=1; i<=n; ++i) s[i]=make(x[i], i);
		sort(s+1, s+n+1);
		for (reg pos1=1,pos2=1,top=1,t1,t2; top<=n; pos1=pos2=++top) {
			while (top+1<=n && s[pos1].fir==s[top+1].fir) ++top;
			pos2=top;
			while (pos1<pos2) {
				t1=min(query(s[pos1].sec-1), query(n)-query(s[pos1].sec));
				t2=min(query(s[pos2].sec-1), query(n)-query(s[pos2].sec));
				if (t1<t2) ans+=t1, del(s[pos1++].sec);
				else ans+=t2, del(s[pos2--].sec);
			}
			ans+=min(query(s[pos1].sec-1), query(n)-query(s[pos1].sec)), del(s[pos1].sec);
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

signed main()
{
	n=read();
	for (int i=1; i<=n; ++i) x[i]=read();
	task::solve();
	
	return 0;
}
posted @ 2021-08-06 08:20  Administrator-09  阅读(17)  评论(0编辑  收藏  举报