P5574 [CmdOI2019]任务分配问题 题解

P5574

分析

发现题目中的无序度相当于是区间内的逆序对数,设 \(cal(l,r)\)\([l,r]\) 的代价,\(dp_i\) 为最后一段末端为 \(i\) 时最小的总代价,转移方程就呼之欲出了:

\[dp_i=\min\limits_{j=1}^{i-1}\{f_j+cal(i,j)\} \]

,其中 \(f_j\) 表示上一层 \(j\) 的 dp 值。容易猜出它满足决策单调性,于是可以用分治优化。而 \(cal\) 函数也可以快速从相邻的推出,用树状数组维护每个值出现的次数,支持单点加、区间求和操作,就可以方便的加入一个新数,并求逆序对个数。dp \(m-1\) 次,每次从上一次的结果转移即可。

核心代码

int n,m,a[MAXN],tr[MAXN],res,L=1,R,dp[MAXN],f[MAXN];
inline int lowbit(int x){return x&(-x);}
void add(int x,int c){for(;x<=n;x+=lowbit(x)) tr[x]+=c;}
int que(int x){int res=0;for(;x>0;x-=lowbit(x)) res+=tr[x];return res;}
int cal(int l,int r){
    while(l<L) L--,res+=que(n)-que(a[L]),add(a[L],1);
    while(r>R) R++,res+=que(a[R]-1),add(a[R],1);
    while(l>L) res-=que(n)-que(a[L]),add(a[L],-1),L++;
    while(r<R) res-=que(a[R]-1),add(a[R],-1),R--;return res;
}
void solve(int l,int r,int pl,int pr){
    if(l>r) return;
    int p=0,mid=(l+r)>>1;dp[mid]=inf;
    for(int i=pl;i<=qmin(pr,mid-1);i++)
        if(f[i]+cal(i+1,mid)<dp[mid]) dp[mid]=f[i]+cal(i+1,mid),p=i;
    solve(l,mid-1,pl,p);solve(mid+1,r,p,pr);
}
signed main(){
    qread(n,m);int i,j;for(i=1;i<=n;i++) qread(a[i]);
    for(i=1;i<=n;i++) dp[i]=f[i]=cal(1,i);m--;
    while(m--){
        solve(1,n,0,n-1);
        for(i=1;i<=n;i++) f[i]=dp[i];
    }printf("%lld\n",dp[n]);
    return 0;
}
posted @ 2022-09-14 19:40  l_x_y  阅读(41)  评论(0编辑  收藏  举报