题解 P5574 【[CmdOI2019]任务分配问题】
command_block · · 题解
官方题解:任务分配问题
人话题面: 给一个序列分成
一道十分套路的DP题。
1~#2
只有一个CPU,写一个树状数组求顺序对就好了,不再赘述。
3
只有两个CPU,枚举分割线取min,树状数组维护两边顺序对即可,不再赘述。
4~#5
不难想到一个dp :
设
转移 : 设
如何计算
注意到,类似莫队,我们能从
那么我们就倒着来转移,不断加入数字,再更新逆序对数,就能做到转移
总的复杂度是
下面的这份代码可以拿到50pts:
#include<algorithm>
#include<cstdio>
#define lowbit(x) ((x)&-(x))
#define MaxN 40500
using namespace std;
int n,k,a[MaxN],c[MaxN];
void clear(){for (int i=1;i<=n;i++)c[i]=0;}
void add(int pos)
{
while(pos<=n)
{c[pos]++;pos+=lowbit(pos);}
}
int query(int pos)
{
int sum=0;
while(pos)
{sum+=c[pos];pos^=lowbit(pos);}
return sum;
}
long long f[45][MaxN];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
long long sav=0;
for (int i=1;i<=n;i++){
sav+=query(a[i]);
f[1][i]=sav;
add(a[i]);
}if (k==1){printf("%lld",f[1][n]);return 0;}
for (int j=2;j<k;j++){
for (int i=j;i<=n;i++){
clear();sav=0;f[j][i]=1ll<<60;
add(a[i]);
for (int t=i-1;t>=j-1;t--){
f[j][i]=min(f[j][i],f[j-1][t]+sav);
sav+=i-t-query(a[t]);
add(a[t]);
}
}
}
clear();sav=0;f[k][n]=1ll<<60;
add(a[n]);
for (int t=n-1;t>=k-1;t--){
f[k][n]=min(f[k][n],f[k-1][t]+sav);
sav+=n-t-query(a[t]);
add(a[t]);
}printf("%lld",f[k][n]);
return 0;
}
6~#10
首先你要知道决策单调性是个什么东西,可以看看DP的决策单调性优化总结,或者上网自行百度。
使用50分的暴力把决策点输出一下,你会发现这东西有决策单调性。
感性理解就是 :
证明:
那么弄一个单调性分治就好了。
至于,
就是类似于莫队那样暴力跳,复杂度是对的。
总复杂度
#include<algorithm>
#include<cstdio>
#define lowbit(x) ((x)&-(x))
#define MaxN 40500
using namespace std;
int n,k,a[MaxN],c[MaxN];
inline void _del(int pos)
{
while(pos<=n)
{--c[pos];pos+=lowbit(pos);}
}
inline void _add(int pos)
{
while(pos<=n)
{++c[pos];pos+=lowbit(pos);}
}
inline int _query(int pos)
{
int sum=0;
while(pos)
{sum+=c[pos];pos^=lowbit(pos);}
return sum;
}
int l=1,r;long long ans;
long long query(int tl,int tr)
{
while(tl<l){
ans+=r-l+1;
ans-=_query(a[--l]);
_add(a[l]);
}
while(r<tr){
ans+=_query(a[++r]);
_add(a[r]);
}
while(l<tl){
_del(a[l]);
ans+=_query(a[l++]);
ans-=r-l+1;
}
while(tr<r){
_del(a[r]);
ans-=_query(a[r--]);
}return ans;
}
int p[27][MaxN];
void solve(long long *f,long long *g,int *sp,int l,int r,int tl,int tr)
{
int mid=(l+r)>>1,p;
long long sav;
f[mid]=1ll<<60;
for (int i=tl;i<=min(tr,mid-1);i++){
sav=g[i]+query(i+1,mid);
if (sav<f[mid])
{ f[mid]=sav; p=i; }
}sp[mid]=p;
if (l<mid)solve(f,g,sp,l,mid-1,tl,p);
if (mid<r)solve(f,g,sp,mid+1,r,p,tr);
}
long long f[27][MaxN];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
for (int i=1;i<=n;i++){
f[1][i]=f[1][i-1]+_query(a[i]);
_add(a[i]);
}for (int i=1;i<=n;i++)c[i]=0;
for (int j=2;j<=k;j++)
solve(f[j],f[j-1],p[j],j,n,j-1,n);
printf("%lld",f[k][n]);
return 0;
}