[AHOI2008] 逆序对

link

我们可以很容易的推断出$-1$是单调不降的,若$i>j$且$a_i$与$a_j$都没有填数,若填完之后$a_i>a_j$或者$a_i<a_j$,则对答案产生影响的只在$[i,j]$之间,则$a_i<a_j$对答案产生的贡献更小,则其实每个不同位置的$-1$其实是互不影响的,所以就可以用$dp$实现

设$dp(i,j)$表示这是从右往左数第$i$个$-1$,这里填j的最小逆序对数(这里的逆序对是只与$-1$有关的,其他的单算)

则$dp(i,j)=min(dp(i-1,p)+在第i个-1左面不是-1的对此数新产生的逆序对数+此数填后对右面产生的贡献) (j \leq p)$

我们可以用线段树维护逆序对,时间复杂度:$O(n\times k^2)$

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read(){
    int f=1,ans=0;char c;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-‘0‘;c=getchar();}
    return f*ans;
}
int n,k,a[10001],cnt[10001],ans[40014];
void add(int k,int l,int r,int x,int y){
    if(x>y) return ;
    if(x<=l&&r<=y){ans[k]++;return;}
    int mid=l+r>>1;
    if(x<=mid) add(k<<1,l,mid,x,y);
    if(mid<y) add(k<<1|1,mid+1,r,x,y);
    ans[k]=ans[k<<1]+ans[k<<1|1];
    return;
}
int query(int k,int l,int r,int x,int y){
    if(x>y) return 0;
    if(x<=l&&r<=y) return ans[k];
    int mid=l+r>>1,res=0;
    if(x<=mid) res+=query(k<<1,l,mid,x,y);
    if(mid<y) res+=query(k<<1|1,mid+1,r,x,y);
    return res;
}
int cost[10001][101],sum,dp[10001][101],tot,minn,inf=2<<30-1;
int main(){
    minn=inf;
    memset(dp,127/3,sizeof(dp));
    n=read(),k=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        if(a[i]==-1)
            cnt[++cnt[0]]=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=k;j++){
            cost[i][j]=cost[i-1][j];
            if(j<=a[i]) cost[i][j]++;
        }
    }
    for(int i=1;i<=k;i++) dp[0][i]=0;
    for(int i=n;i>=1;i--){
        if(a[i]!=-1){
            sum+=query(1,1,k,1,a[i]-1);
            add(1,1,k,a[i],a[i]);
        }else{
            tot++;
            for(int j=1;j<=k;j++){
                for(int p=j;p<=k;p++){
                    dp[tot][j]=min(dp[tot-1][p]+query(1,1,k,1,j-1)+cost[i][j+1],dp[tot][j]);
                    if(tot==cnt[0]) minn=min(minn,dp[tot][j]);
                }
            }
        }
    }
    if(minn==inf) cout<<sum;
    else cout<<sum+minn;
}

原文地址:https://www.cnblogs.com/si-rui-yang/p/9907506.html

时间: 2024-10-10 15:39:51

[AHOI2008] 逆序对的相关文章

动态规划 BZOJ1831 [AHOI2008]逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 660  Solved: 469[Submit][Status][Discuss] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个"逆序对".你数一数下面的数字里有多少个逆序

BZOJ1831: [AHOI2008]逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 341  Solved: 226[Submit][Status] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”.你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离

BZOJ1786: [Ahoi2008]Pair 配对/1831: [AHOI2008]逆序对

这两道题是一样的. 可以发现,-1变成的数是单调不降. 记录下原有的逆序对个数. 预处理出每个点取每个值所产生的逆序对个数,然后dp转移. #include<cstring> #include<iostream> #include<cstdio> #include<map> #include<cmath> #include<algorithm> #define rep(i,l,r) for (int i=l;i<=r;i++)

bzoj1786: [Ahoi2008]Pair 配对&amp;&amp;1831: [AHOI2008]逆序对

一个自以为很对的东西,我们往-1放的数肯定是不增的. 然后就预处理一下,假如i这个位置放j会多多少逆序对. DP一下,我的复杂度应该是O(n*m^2)的,然而你随便搞都能省掉一个m吧,我算了算好像可以过就不管了. 注意树状数组的时候getsum是a[i]-1,相同是不算逆序对的 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorit

P4280 [AHOI2008]逆序对

传送门 有一个不会证明的贪心:从左到右考虑每一个位置,然后在每一个位置都贪心选取能让该位置构成的逆序对最少的数.判断逆序对的话只要记一下前缀小于等于某数的总数和后缀小于等于某数的总数就行了 //minamoto #include<bits/stdc++.h> #define rint register int #define inf 0x3f3f3f3f #define ll long long using namespace std; #define getc() (p1==p2&&

【[AHOI2008]逆序对】

被锤爆了 被这个题搞得自闭了一上午,觉得自己没什么前途了 我又没有看出来这个题的一个非常重要的性质 我们填进去的数一定是单调不降的 首先如果填进去的数并不是单调不降的,那么填进去本身就会产生一些逆序对,感性理解好像是单调不降更优,这里还是严谨证明一下吧 考虑一下树状数组求逆序对的过程,显然就是求出每一个数前面有多少个比它大的数 这张图好丑啊 设\(A<B\),\(x\)表示那段绿色区间里大于\(A\)的数,\(y\)表示绿色区间里大于\(B\)的数,\(a\)表示蓝色区间里大于\(A\)的数,\

bzoj1831【AHOI2008】逆序对

1831: [AHOI2008]逆序对 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 485  Solved: 341 [Submit][Status][Discuss] Description 小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远.好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间.如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个"逆序对".你数一数下面的数字里有多少个

BZOJ1786 [Ahoi2008]Pair 配对 动态规划 逆序对

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1786 题意概括 给出长度为n的数列,只会出现1~k这些正整数.现在有些数写成了-1,这些-1可以变成任何数. 求把这些-1变成1~k中的正整数之后,最少的逆序对个数为多少. 题解 我们可以判断,这些-1中写的数字一定是单调不降的. 为什么?我们把答案序列的所有-1位抽出来,如果答案序列中有一组是逆序的,那么交换他们,一定可以保证小的那个换到大的那个的位置的时候,它左右产生的逆序对数一定比大的原先

codeforces 414C C. Mashmokh and Reverse Operation(归并排序求逆序对)

题目链接: C. Mashmokh and Reverse Operation time limit per test 4 seconds memory limit per test 512 megabytes input standard input output standard output Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university