2018.8.4T2(贪心,dp,线段树,优先队列)

给定一个偶数 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就不讲了
f [ i ] [ j ] 表示奇数项选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;
} 

但是优先队列部分不是很好想
s o l u t i o n
我们考虑每个数字x放在i后面的贡献
= j = 1 i [ b [ j ] > x ] + j = i + 1 n [ b [ j ] < x ]
把后面那一项换一下就变成了
= j = 1 i [ b [ j ] > x ] + x 2 j = i j [ b [ j ] < x ]
= x 2 + j = 1 i [ b [ j ] > x ] [ b [ j ] < x ]
我们考虑 x 变成 x + 2
唯一有变化的就是 x + 1
会从左边变到右边,答案变为 2
也就是一个原数列中的数对答案的影响相当于是一个后缀
到这里我们就能发现答案具有单调性
然后我们就可以分开算答案就行了

#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;
} 

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/81416349