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