zoukankan      html  css  js  c++  java
  • Gym

    题意:x轴上有n个人,让你放置m个集合点,使得每个人往离他最近的集合点走,所有人走的距离和最短。

    把距离视为花费,设$dp[i][k]$表示前i个人分成k段的最小花费,则有递推式$dp[i][k]=min{dp[j][k-1]+w(j,i)}$,其中$w(j,i)$可以$O(1)$求出。

    显然,如果考虑段数的话,光状态数就有n^2个,肯定行不通。不过这题的最优解对段数的函数是凸的,因此可以用WQS二分来打破段数的限制。

    给每个集合点加上一个额外的花费c,然后忽略段数的限制,这样递推式就变成了$dp[i]=min{dp[j]+w(j,i)}+c$,这个递推式满足“决策单调性”,即如果i是由j转移而来,而i'>i,则j'>=j。这种dp是有一定的套路的,利用单调队列维护可能成为最优决策点的点以及它的左右边界,中间过程中需要不断地“掐头去尾”,及时弹出队首已经废掉的决策点,每push进一个结点,需要弹出队尾不如它优的决策点,并修改队尾的右边界,保证队首总是最优决策点。值得注意的是这道题的最优决策边界不像斜率优化那样明显可以直接算出来,也需要通过二分来确定。

    然后在dp的过程中记录段数cnt[n],如果最优解分成了k段,那么dp[n]-k*c就是在划分为k段的条件下的最优解。根据k与m的大小关系进行二分,直到最优解恰好分成了m段为止。

    你如果问为什么满足凸性和决策单调性?蒟蒻表示不会证,反正凭经验和直觉猜就对了,或者打表~~

    复杂度$O(nlognlogA)$,这道题还要和卡常斗智斗勇,算法常数过大会T,变量全开longlong也会T...

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=3e5+10,inf=0x3f3f3f3f;
     5 int n,m,hd,tl,a[N],q[N],cnt[N],L[N],R[N];
     6 ll S[N],dp[N],k;
     7 ll sum(int l,int r) {return S[r]-S[l-1];}
     8 ll w(int i,int j) {
     9     ++i;
    10     return sum((i+j+2)>>1,j)-sum(i,(i+j-1)>>1)+k;
    11 }
    12 int fd(int j,int k) {
    13     int ret=R[k]+1,l=L[k],r=R[k];
    14     while(l<=r) {
    15         int mid=(l+r)>>1;
    16         if(dp[j]+w(j,mid)<=dp[k]+w(k,mid))ret=mid,r=mid-1;
    17         else l=mid+1;
    18     }
    19     return ret;
    20 }
    21 int solve() {
    22     hd=tl=0,L[0]=1,R[0]=n,q[tl++]=0;
    23     for(int i=1; i<=n; ++i) {
    24         for(; hd<tl&&R[q[hd]]<i; ++hd);
    25         dp[i]=dp[q[hd]]+w(q[hd],i);
    26         cnt[i]=cnt[q[hd]]+1;
    27         for(; hd<tl&&dp[i]+w(i,L[q[tl-1]])<=dp[q[tl-1]]+w(q[tl-1],L[q[tl-1]]); --tl);
    28         L[i]=(hd<tl?fd(i,q[tl-1]):i+1),R[i]=n;
    29         if(hd<tl)R[q[tl-1]]=L[i]-1;
    30         q[tl++]=i;
    31     }
    32     return cnt[n];
    33 }
    34 ll bi(ll l,ll r) {
    35     ll ret;
    36     while(l<=r) {
    37         ll mid=(l+r)>>1;
    38         k=mid;
    39         if(solve()>=m)ret=dp[n]-m*k,l=mid+1;
    40         else r=mid-1;
    41     }
    42     return ret;
    43 }
    44 int main() {
    45     scanf("%d%d",&n,&m);
    46     for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
    47     for(int i=1; i<=n; ++i)S[i]=S[i-1]+a[i];
    48     printf("%lld
    ",bi(0,S[n]+10));
    49     return 0;
    50 }
  • 相关阅读:
    ip addr add 192.168.1.5 peer 192.168.1.6 dev enahisic2i1
    mininet对象添加带外管理
    mininet + mnec +bgpd
    Exception: Could not find a default OpenFlow controller
    mininet xterm图像化
    哇塞的Docker——vscode远程连接Docker容器进行项目开发(三)
    DJB Hash Function,也称times33算法, php的实现与分析-算法
    特别好用的音乐软件(亲测有效)
    实验室第一次排位赛
    C语言 编写“剪刀石头布”小游戏
  • 原文地址:https://www.cnblogs.com/asdfsag/p/11824458.html
Copyright © 2011-2022 走看看