给定一个偶数 N,现在蔡老板得到了一个由 [1,N] 内的所有偶数构成的排列 b[1..N/2]
现在蔡老板又得到了一个数组 a[1..N/2],其中 a[i]=i∗2−1
蔡老板想知道,对于所有满足 a 和 b 都是它的子序列的 [1,N] 的排列 p,p 的逆序对的最小值
输入格式
第一行一个偶数 N
第二行 N/2N/2 个偶数,描述 b[1..N/2]
输出格式
输出逆序对的最小值
样例1
input
6
6 4 2
output
5
50分做法dp就不讲了
表示奇数项选i个偶数项选j个,瞎转移就好
考虑满分的贪心
solution①
考虑每个偶数项,他在奇数项中间肯定有个原来的位置贡献的逆序对为1
相当于我们把原来的位置移成输入的位置
每次移动有花费
反过来就是把输入花费最少移成不下降序列
我们可以用优先队列来做
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int b[200100];
int n;
int tr[200100];
priority_queue<int>q;
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
int query(int x)
{
int sum = 0;
for(;x;x -= x & (-x))
sum += tr[x];
return sum;
}
void add(int x,int v)
{
for(;x <= n;x += x & (-x))
tr[x] += v;
return;
}
int main()
{
n = read()/2;
ll ans = 0;
for(int i = 1;i <= n;++i) b[i] = read() / 2;
for(int i = n;i >= 1;--i)
{
ans += query(b[i]);
add(b[i] , 1);
}
q.push(b[1]);
for(int i = 2;i <= n;++i)
{
if(q.top() > b[i])
{
ans += q.top() - b[i];
q.pop();
q.push(b[i]);
}
q.push(b[i]);
}
printf("%lld\n",ans);
return 0;
}
但是优先队列部分不是很好想
我们考虑每个数字x放在i后面的贡献
把后面那一项换一下就变成了
我们考虑
变成
唯一有变化的就是
会从左边变到右边,答案变为
也就是一个原数列中的数对答案的影响相当于是一个后缀
到这里我们就能发现答案具有单调性
然后我们就可以分开算答案就行了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls root * 2
#define rs root * 2 + 1
int n ;
int b[101000];
int pos[101000];
ll ans;
int minn[401000] , lazy[401000];
int tr[101000];
const int INF = 0x7fffffff;
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
int query(int x)
{
int sum = 0;
for(int i = x;i;i -= i & (-i))
sum += tr[i];
return sum;
}
void add(int x,int v)
{
for(;x <= n;x += x & (-x))
tr[x] += v;
return;
}
void build(int root,int l,int r)
{
minn[root] = INF;
if(l == r)
{
minn[root] = l;
return;
}
int mid = (l + r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
minn[root] = min(minn[ls],minn[rs]);
return;
}
void init()
{
n = read()/2;
for(int i = 1;i <= n;++i)
{
b[i] = read()/2;
pos[b[i]] = i;
}
for(int i = n;i >= 1;--i)
{
ans += query(b[i]);
add(b[i],1);
}
build(1,0,n);
return;
}
void update(int root)
{
minn[ls] += lazy[root];
minn[rs] += lazy[root];
lazy[ls] += lazy[root];
lazy[rs] += lazy[root];
lazy[root] = 0;
return;
}
void change(int root,int l,int r,int x,int y,int v)
{
if(r < x || l > y) return;
if(l == r)
{
minn[root] += v;
return;
}
if(x <= l && r <= y)
{
minn[root] += v;
lazy[root] += v;
return;
}
update(root);
int mid = (l + r)>>1;
change(ls,l,mid,x,y,v);change(rs,mid+1,r,x,y,v);
minn[root] = min(minn[ls] , minn[rs]);
return;
}
void work()
{
for(int i = 1;i <= n;++i)
{
ans += minn[1];
change(1,0,n,0,pos[i]-1,1);
change(1,0,n,pos[i],n,-1);
}
printf("%lld\n",ans);
return;
}
int main()
{
init();
work();
return 0;
}