汕头市队赛 SRM16 T2

描述

猫和老鼠,看过吧?猫来了,老鼠要躲进洞里。在一条数轴上,一共有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;
}

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;
}

时间: 2024-10-07 23:05:54

汕头市队赛 SRM16 T2的相关文章

汕头市队赛 SRM1X T2 ——扫描线

绵津见-终 SRM 13 背景 "西瓜也是可以种在海上的!"--绵津见 然而种在海上的西瓜最需要防范的,是时不时会涌向瓜田的阵阵海浪. 幸好,身为海神的绵津见可以释放魔法"水平如镜"来阻止海浪拍打西瓜. 然而,当西瓜一个接一个成熟之时,它们就要离开瓜田,飘向遥远的彼岸.绵津见的魔法无法保护离开瓜田的西瓜们,但至少,也得知道西瓜们遭遇了多大的风浪啊. 描述 我们用一个坐标系来描述大海,绵津见的瓜田位于x轴下方,每当有一个西瓜成熟时,它会从x轴上一点出发,沿一条平行y轴

汕头市队赛 SRM13 T2

这道题很容易想到是二分 但是因为可能会爆LL 所以要加一波特判 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const LL M=1e6+7,mx=1e18; LL read(){ LL ans=0,c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>

汕头市队赛 SRM16

T3 C-2 SRM 16 描述 给一个数列,给出两种数字, 询问在多少个非空区间中这两种数字出现次数相同. 输入格式 第一行:一个数字n,q,n表示数列长度,q表示q组询问 第二行n个数字表示数列A 接下来q行每行2个数字表示询问 输出格式 输出q行分别对应每个问题的答案 样例输入 2 1 1 2 1 2 样例输出 1 数据范围与约定 n <= 5000,q <= 10000 其他数字在int范围内 样例解释 只有区间[1,2]符合 -------------------------- 因为

汕头市队赛 SRM14 T2 最长上升子序列

最长上升子序列 (tree.pas/c/cpp) 128MB 1s 有一个长度为n的序列a[i],其中1到n的整数各自在a[i]中出现恰好一次. 现在已知另一个等长的序列f[i],表示a[i]中以第i个位置结尾的最长上升子序列的长度,请还原出a[i]. 输入格式 第一行一个正整数n. 接下来一行n个数,其中第i个数表示f[i]. 输出格式 一行,n个整数,表示序列a[i],如果答案不唯一,任意输出一种. 样例输入 7 1 2 3 2 4 4 3 样例输出 1 4 5 2 7 6 3 样例解释 以

汕头市队赛 C KMP codeforces B. Image Preview

汕头市队赛题目传送门 codeforces题目传送门 这道题我的做法是 尝试先往左走然后往右走 或者先往右走然后往左走 然后注意一下枚举顺序就okay啦 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=1e6+7; LL read(){ LL ans=0,f=1,c=getchar(); while(c

汕头市队赛SRM15

T1--czl SRM 15 众所周知,czl家养了一只可♂爱的***(已屏蔽),那只东西很贪吃,所以czl家很多零食仓库,然而这些仓库里有很多老鼠. 为了心爱的***,czl决定点燃纯艾条,用烟熏老鼠. 共有N个仓库,编号1-N. 假设陵陵在第i个仓库点燃艾条,烟雾就会充满该仓库,并向左右扩散Ai 的距离,接着所有|i-j|<=Ai 的仓库 j 的老鼠被消灭. 陵陵是个爱护环境的人,他想知道最少需要多少支艾条,才可以消灭所有老鼠. [输入格式] 第一行:一个正整数,代表 N. 第二行:N 个非

汕头市队赛 SRM13 T3

这道题可以贪心 维护一个答案队列 枚举位置 每次将比当前位置大的队尾全部替代掉 记录删了多少了就好了 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<string> #define LL long long using namespace std; const int M=1e7+7; int n,k,cnt; char s[M]

汕头市队赛 SRM 09 A 撕书

A 撕书I-3 SRM 09 背景&&描述 琉璃在撕书.     书总共有n页,都悬浮在数轴上,第i页的位置为,上面写着一个数字.     琉璃从右往左撕书.假如看到了第i页,就把在第i页左边,且与之距离<=的书都撕掉.(第i页本身不撕)     夜子为了尽量地保全魔法书,决定偷偷在琉璃开始撕之前,增加一页.增加的这一页必须在所有书页的右边,数字随意.     夜子想知道,最少会有多少页书被撕毁. 输入格式 第一行一个整数n,表示书页数. 接下来n行,第i行的俩整数分别为和. 输出格

汕头市队赛 SRM 09 B 撕书

B 撕书II-3 SRM 09 背景&&描述 琉璃手头有一黑一白两本魔法书,一本是<缟玛瑙的不在证明>,另一本是<白色相簿1.5>     传说同时打开这两本书会有奇怪的事情发生.     琉璃打开一看,果然非常奇怪:两本书上都各自写着一个正整数(可能他买到盗版了),分别是a和b.     试图撕书的汀想借过来看看,但琉璃只告诉了他这俩数加起来的值x和异或起来的值y.     汀发现有很多种(a,b)满足琉璃告诉他的信息...你能帮他算出来有多少种吗? 输入格式 两