1. 程式人生 > >汕頭市隊賽 SRM16 T2

汕頭市隊賽 SRM16 T2

如果 ide nbsp 分享 eight clu const -a inf

描述

貓和老鼠,看過吧?貓來了,老鼠要躲進洞裏。在一條數軸上,一共有n個洞,位置分別在xi,能容納vi只老鼠。一共有m只老鼠位置分別在Xi,要躲進洞裏,問所有老鼠跑進洞裏的距離總和最小是多少。

輸入格式

兩個用空格隔開的整數m和n。

這一行m個數字分別表示老鼠的位置

接下來n行每行兩個數字分別表示洞的位置和容納量

輸出格式

一個整數,表示最小的距離總和。(如果無解,輸出-1)

樣例輸入

4 5
6 2 8 9
3 6
2 1
3 6
4 7
4 7

樣例輸出

11
——————————————————————————
n <= 500, m <= 500 的時候可以寫費用流 但是要比較好的建圖方式
nm的建圖肯定要掛
技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int M=2e3+7,inf=0x3f3f3f3f;
const LL mx=1e15;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<0||c>9) {if(c==-) f=-1; c=getchar();}
    while(c>=0&&c<=
9) {ans=ans*10+(c-0); c=getchar();} return ans*f; } int n,m,q[M],vis[M]; int N,S,T; LL ans,d[M]; struct node{int to,next,flow;LL cost;}e[M*M]; int first[M],cnt=1,cur[M]; void ins(int a,int b,int flow,LL cost){e[++cnt]=(node){b,first[a],flow,cost}; first[a]=cnt;} void insert(int a,int b,int flow,LL cost){ins(a,b,flow,cost); ins(b,a,0
,-cost);} bool spfa(){ for(int i=S;i<=T;i++) d[i]=mx; int head=0,tail=1; q[0]=T; vis[T]=1; d[T]=0; while(head!=tail){ int x=q[head++]; if(head>M) head=0; for(int i=first[x];i;i=e[i].next){ int now=e[i].to; if(e[i^1].flow&&d[x]+e[i^1].cost<d[now]){ d[now]=d[x]+e[i^1].cost; if(!vis[now]){ if(d[now]<d[q[head]]){head--; if(head<0) head=M; q[head]=now;} else{q[tail++]=now; if(tail>M) tail=0;} vis[now]=1; } } } vis[x]=0; } return d[S]<mx; } int dfs(int x,int a){ if(x==T||a==0)return a; vis[x]=1; int flow=0,f; for(int& i=cur[x];i;i=e[i].next){ int now=e[i].to; if(!vis[now]&&d[x]==e[i].cost+d[now]&&(f=dfs(now,min(a,e[i].flow)))>0){ e[i].flow-=f; e[i^1].flow+=f; ans+=e[i].cost*f; flow+=f; a-=f;if(a==0)break; } } vis[x]=0; return flow; } int x[M]; LL sum; struct pos{int y,k;}qq[M]; bool cmp(pos a,pos b){return a.y<b.y;} int main() { n=read(); m=read(); S=0; T=n+m+1; for(int i=1;i<=n;i++) x[i]=read(),insert(S,i,1,0); for(int i=1;i<=m;i++) qq[i].y=read(),qq[i].k=read(),sum+=qq[i].k; if(sum<n){printf("-1\n"); return 0;} sort(qq+1,qq+1+m,cmp); for(int i=1;i<=m;i++) insert(i+n,T,qq[i].k,0); for(int i=1;i<=m;i++){ if(i>1) insert(i+n,i+n-1,inf,qq[i].y-qq[i-1].y); if(i<m) insert(i+n,i+n+1,inf,qq[i+1].y-qq[i].y); } for(int i=1;i<=n;i++){ int k1=1; while(qq[k1+1].y<=x[i]&&k1<m) k1++; int k2=m; while(qq[k2-1].y>=x[i]&&k2>1) k2--; if(qq[k1].y<=x[i]) insert(i,k1+n,1,x[i]-qq[k1].y); if(qq[k2].y>=x[i]) insert(i,k2+n,1,qq[k2].y-x[i]); } while(spfa()){for(int i=0;i<=T;i++) cur[i]=first[i]; dfs(S,inf);} printf("%lld\n",ans); return 0; }
View Code

n <= 5000, m <= 5000 的時候就需要dp了

先將洞和老鼠按位置從小到大排一波序

因為老鼠選的洞必然是單調遞增的 我們可以考慮dp

f【i】【j】表示前i個洞選j只老鼠

轉移方程 f【i】【j】=f【i-1】【k】+(k+1到j 的距離

然後發現是三方的寫法 然後就後面可以用單調隊列優化dp降一波復雜度

然後就是n方寫法了

這裏我們每次可以算一下每只老鼠到 第i個洞的 前綴

隊列裏扔的就是f【j】+(前綴數組)s【j】就好啦

f【i】=q【l】+s【i】就好辣

技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int M=5e3+7;
const LL inf=1e15;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<0||c>9) {if(c==-) f=-1; c=getchar();}
    while(c>=0&&c<=9) {ans=ans*10+(c-0); c=getchar();}
    return ans*f;
}
int l,r,n,m,x[M];
struct pos{int y,k;}e[M];
bool cmp(pos a,pos b){return a.y<b.y;}
LL sum,f[M],s[M];
LL pabs(LL x){return x>=0?x:-x;}
struct node{LL v; int pos;}q[M];
int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++) x[i]=read();
    for(int i=1;i<=m;i++) e[i].y=read(),e[i].k=read(),sum+=e[i].k;
    if(sum<n){printf("-1\n"); return 0;}
    sort(x+1,x+1+n);
    sort(e+1,e+1+m,cmp);
    for(int i=1;i<=n;i++) f[i]=inf;
    for(int i=1;i<=m;i++){
        l=1; r=0;
        for(int j=1;j<=n;j++) s[j]=s[j-1]+pabs(x[j]-e[i].y);
        for(int j=0;j<=n;j++){
            while(l<=r&&q[r].v>=f[j]-s[j]) r--;
            while(l<=r&&(j-q[l].pos)>e[i].k) l++;
            q[++r].v=f[j]-s[j]; q[r].pos=j;
            f[j]=q[l].v+s[j];
        }
    }printf("%lld\n",f[n]);
    return 0;
}
View Code

汕頭市隊賽 SRM16 T2