zoukankan      html  css  js  c++  java
  • [CmdOI2019]任务分配问题

    LXXVII.[CmdOI2019]任务分配问题

    这道题与LXI.CF868F Yet Another Minimization Problem长得很像。实际算法也类似。

    首先,题意就是把所有数划分成\(k\)段,使得每段内部正序对数量之和最少。设\(w(i,j)\)表示区间\((i,j)\)内部正序对数量。则很轻松就能得到

    \[w(i-1,j+1)+w(i,j)\geq w(i,j+1)+w(i-1,j) \]

    因为其它所有正序对都在两个中被统计了,唯独\((i-1,j+1)\)的正序对只有可能在前一半中被统计。因此此式显然成立,即四边形不等式成立,可以使用决策单调性优化。

    因此直接套用分治+类似莫队的\(w\)求法即可。复杂度\(O(nk\log^2n)\)

    代码(将正序对转换成了逆序对):

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long lol;
    int n,m,t[25010],a[25010],ll,rr;
    lol res,f[25010],g[25010];
    void add(int x,int y){
    	while(x<=n)t[x]+=y,x+=x&-x;
    }
    int ask(int x){
    	int rt=0;
    	while(x)rt+=t[x],x-=x&-x;
    	return rt;
    }
    lol calc(int l,int r){
    	if(l>r)return 0x3f3f3f3f3f3f3f3f;
    	while(l>ll)add(a[ll],-1),res-=ask(a[ll]),ll++;
    	while(l<ll)--ll,res+=ask(a[ll]),add(a[ll],1);
    	while(r<rr)add(a[rr],-1),res-=ask(n)-ask(a[rr]),rr--;
    	while(r>rr)++rr,res+=ask(n)-ask(a[rr]),add(a[rr],1);
    	return res;
    }
    void func(int l,int r,int L,int R){
    	if(l>r||L>R)return;
    	int mid=(l+r)>>1,mp;
    	lol mn=0x3f3f3f3f3f3f3f3f;
    	for(int i=L;i<=R;i++)if(f[i]+calc(i+1,mid)<mn)mn=f[i]+calc(i+1,mid),mp=i;
    	g[mid]=mn;
    	func(l,mid-1,L,mp),func(mid+1,r,mp,R);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]=n-a[i]+1,res+=ask(n)-ask(a[i]),f[i]=res,add(a[i],1);
    	ll=1,rr=n;
    	while(--m)func(1,n,0,n-1),memcpy(f,g,sizeof(f));
    	printf("%lld\n",f[n]);
    	return 0;
    }
    

  • 相关阅读:
    Java vs Python
    Compiled Language vs Scripting Language
    445. Add Two Numbers II
    213. House Robber II
    198. House Robber
    276. Paint Fence
    77. Combinations
    54. Spiral Matrix
    82. Remove Duplicates from Sorted List II
    80. Remove Duplicates from Sorted Array II
  • 原文地址:https://www.cnblogs.com/Troverld/p/14598431.html
Copyright © 2011-2022 走看看