1. 程式人生 > >[AHOI2008] 逆序對

[AHOI2008] 逆序對

amp ahoi2008 lse 產生 逆序對數 有關 技術 getchar() query

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; }
View Code

[AHOI2008] 逆序對