XSY3244 10.31 D
阿新 • • 發佈:2018-10-31
XSY3244 10.31 D
題意:
數軸上有\(N\)只老鼠\(M\)個洞,每個洞有一個容量,求所有老鼠進洞的最小代價。(\(N,M\leq1000000\),時限\(2s\))
題解:
被代爺的前兩道題卡得醉生夢死,場上根本沒看這題。。。
十萬的檔顯然可以\(dp\),加個線段樹什麼的就可以了。
對於100%的資料,上面的那個\(dp\)已經沒用了,代爺給的做法是正反貪心,讓每隻老鼠貪心選擇左/右最近的洞。對於每隻老鼠,這兩個洞正好是它最終決策會去的洞。然後把老鼠和每一個可能有老鼠進的洞一起擺在數軸上,做一遍\(dp\)。狀態\(f_{i,j}\)表示搞到第\(i\)個點,老鼠比洞多\(j\)
場上還有人想出了另一種做法。這是我程式碼的做法。
我們建出一個模型:
對於老鼠和洞,我們分別用兩個大根堆\(M,H\)儲存,按位置。
將老鼠和洞按位置排序。
如果當前處理的是老鼠,那就取\(H\)堆頂,欽定它進這個洞,這個容量\(-1\),並且在以老鼠為鏡面,這個洞反射過去的地方往\(M\)堆裡插入一隻老鼠,更新答案。
如果當前處理的是洞,那就讓\(M\)堆中所有座標比它大的老鼠滾進這個洞(顯然比老鼠在當前洞內更優),但是這不一定最優,所以我們要建立反悔機制:記\(\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; }