1. 程式人生 > >XSY3244 10.31 D

XSY3244 10.31 D

XSY3244 10.31 D

題意:

​ 數軸上有\(N\)只老鼠\(M\)個洞,每個洞有一個容量,求所有老鼠進洞的最小代價。(\(N,M\leq1000000\),時限\(2s\))

題解:

​ 被代爺的前兩道題卡得醉生夢死,場上根本沒看這題。。。

​ 十萬的檔顯然可以\(dp\),加個線段樹什麼的就可以了。

​ 對於100%的資料,上面的那個\(dp\)已經沒用了,代爺給的做法是正反貪心,讓每隻老鼠貪心選擇左/右最近的洞。對於每隻老鼠,這兩個洞正好是它最終決策會去的洞。然後把老鼠和每一個可能有老鼠進的洞一起擺在數軸上,做一遍\(dp\)。狀態\(f_{i,j}\)表示搞到第\(i\)個點,老鼠比洞多\(j\)

個,轉移挺簡單的。這個\(dp\)相較上面的那個,優點在於它可以直接利用指標跳\(O(1)\)轉移。時空複雜度都是線性的。

​ 場上還有人想出了另一種做法。這是我程式碼的做法。

​ 我們建出一個模型:

  • 對於老鼠和洞,我們分別用兩個大根堆\(M,H\)儲存,按位置。

  • 將老鼠和洞按位置排序。

  • 如果當前處理的是老鼠,那就取\(H\)堆頂,欽定它進這個洞,這個容量\(-1\)並且在以老鼠為鏡面,這個洞反射過去的地方往\(M\)堆裡插入一隻老鼠,更新答案。

  • 如果當前處理的是洞,那就讓\(M\)堆中所有座標比它大的老鼠滾進這個洞(顯然比老鼠在當前洞內更優),但是這不一定最優,所以我們要建立反悔機制:\(\Delta = 當前洞的座標-這隻老鼠的座標\)

    ,在\(當前洞座標當前洞座標+ \Delta\)的地方新建容量為一的洞;更新答案,如果處理完所有之後當前洞還有剩餘的容量,就把它扔進堆裡。想一想,為什麼。

    ​程式碼:

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define fo(i,l,r) for(int i=l;i<=r;i++)
    #define of(i,l,r) for(int i=l;i>=r;i--)
    #define fe(i,u) for(int i=head[u];i;i=e[i].next)
    using namespace std;
    typedef long long ll;
    typedef pair<ll,int> pli;
    #define P(a,b) make_pair(a,b)
    inline void open(const char *s)
    {
      #ifndef ONLINE_JUDGE
      char str[20];
      sprintf(str,"in%s.txt",s);
      freopen(str,"r",stdin);
    //    sprintf(str,"out%s.txt",s);
    //    freopen(str,"w",stdout);
      #endif
    }
    inline ll rd()
    {
      static ll x,f;
      x=0;f=1;
      char ch=getchar();
      for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1ll;
      for(;ch>='0'&&ch<='9';ch=getchar())x=x*10ll+ch-'0';
      return f>0?x:-x;
    }
    const int N=1000010;
    int n,m;
    ll ans=0;
    pli a[N<<1];
    priority_queue<ll>M;
    priority_queue<pli>H;
    
    inline void gaoM(pli x)
    {
      ll res=1000000000000000ll;
      if(!H.empty()){
          pli y=H.top();H.pop();
          res=x.first-y.first;
          if(--y.second)H.push(y);
      }
      M.push(x.first+res);
      ans+=res;
    }
    inline void gaoH(pli x)
    {
      while(x.second&&!M.empty()&&M.top()>x.first){
          ll y=M.top();M.pop();
          ll res=x.first-y;x.second--;
          ans+=res;H.push(P(x.first+res,1));
      }
      if(x.second)H.push(x);
    }
    
    int main()
    {
      open("c");
      n=rd();m=rd();
      fo(i,1,n)a[i].first=rd(),a[i].second=-1;
      ll s=0;
      fo(i,1,m)a[n+i].first=rd(),a[n+i].second=rd(),s+=a[n+i].second;
      if(s<n)return puts("-1"),0;
      sort(a+1,a+n+m+1);
      fo(i,1,n+m){
          if(a[i].second==-1)gaoM(a[i]);
          else gaoH(a[i]);
      }
      printf("%lld\n",ans);
      return 0;
    }