51NOD1781 Pinball 【离散化+线段树】

9 篇文章 0 订阅
3 篇文章 0 订阅

1781 Pinball

基准时间限制:1 秒 空间限制:262144 KB 分值: 20 难度:3级算法题

Pinball的游戏界面由m+2行、n列组成。第一行在顶端。一个球会从第一行的某一列出发,开始垂直下落,界面上有一些漏斗,一共有m个漏斗分别放在第2~m+1行,第i个漏斗的作用是把经过第i+1行且列数在Ai~Bi之间的球,将其移到下一行的第Ci列。 使用第i个漏斗需要支付Di的价钱,你需要保留一些漏斗使得球无论从第一行的哪一列开始放,都只可能到达第m+2行的唯一 一列,求花费的最少代价。

(样例的图)
这里写图片描述
(我们保留2,4,5即可,代价为5+3+12=20)

Input

第一行两个数,m和n。m<=100000,2<=n<=1000000000
接下来m行,第i+1行描述第i个漏斗的属性,Ai,Bi,Ci,Di (1<=Ai<=Ci<=Bi<=n, 1<=Di<=1000000000)。

Output

若不存在一种方案能满足条件则输出-1,否则输出最小花费

Input示例

5 6
3 5 4 8
1 4 3 5
4 6 5 7
5 6 5 3
3 5 4 12

Output示例

20

xfause (题目提供者)


首先 需要发现 对任意一种漏斗组合
设to[i] = 从第i列扔下小球 小球最终掉落的位置
对任意i < j ,必定有 to[i] <= to[j]
所以 如果 to[1]==to[m] 小球从任意位置掉落 都到 to[1] 位置上

所以 问题转化为 使1和m位置扔下的小球 落在同样位置

不难发现 花销最小时 1和m扔出的球 都会经过最后一个漏斗 且 在之前 不会经过相同的漏斗

oneTo[i] = 从1扔出 经过第i个漏斗 需要的最少花费
mTo[i] = 从m扔出 经过i漏斗 的最少花费

所以 答案就 = min( oneTo[i] + mTo[i] - d[i] )

于是问题就转化为求oneTo[i] 和 mTo[i]
显然

oneTo[i] = min( oneTo[j] ) + d[i]     //其中a[i]<=c[j]<=b[i] 且 j < n
oneTo[i] = d[i]                       //a[i]==1

复杂度为O(n*n) 显然不理想

此处如果使用线段树 维护 从1扔出小球 从 [ posL,posR ] 位置掉出 的最小花销
每次查询O(log(n) )
于是复杂度为O(nlog(n) )

PS:显然m过大 果断离散化

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<time.h>
#include<math.h>
#include<list>
#include<cstring>
#include<fstream>
#include<queue>
#include<sstream>
//#include<memory.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define INF 1000000007
#define pll pair<ll,ll>
#define pid pair<int,double>

const ll inf = 1e17;
const int N = 100000 + 5;
const int M = 3*N;

int read(){
    char c=getchar();int k=0;for (;c<48||c>57;c=getchar());
    for (;c>47&&c<58;c=getchar()) k=(k<<3)+(k<<1)+c-48;return k;
}

ll rmq[M*4];//从[l,r]列掉下一个小球的最少花费
int pot[N][4];

struct ColHash{
    int key,i,j;
}colHash[3*N];

bool operator<(const ColHash&a,const ColHash&b){
    return a.key<b.key;
}

int Hash(int n,int m){//返回离散化后的列数
    colHash[3*n]={1,-1,-1};
    colHash[3*n+1]={m,-1,-1};
    for(int i=0;i<n;++i){
        for(int j=0;j<3;++j){
            colHash[3*i+j]={pot[i][j],i,j};
        }
    }
    sort(colHash,colHash+3*n+2);
    int k=0;
    for(int i=0;i<3*n+2;++i){
        if(i==0||colHash[i].key!=colHash[i-1].key){
            ++k;
        }
        if(colHash[i].i!=-1){
            pot[colHash[i].i][colHash[i].j]=k;
        }
    }
    return k;
}

void initRmq(int k,int l,int r){
    rmq[k]=inf;
    if(l!=r){
        int mid = (l+r)/2;
        initRmq(2*k+1,l,mid);
        initRmq(2*k+2,mid+1,r);
    }
}

void addNode(int k,int l,int r,int pos,ll v){
    if(l<=pos&&pos<=r){
        rmq[k]=min(rmq[k],v);
        if(l!=r){
            int mid=(l+r)/2;
            if(pos<=mid){
                addNode(2*k+1,l,mid,pos,v);
            }
            if(pos>=mid+1){
                addNode(2*k+2,mid+1,r,pos,v);
            }
        }
    }
}

ll query(int k,int l,int r,int posL,int posR){
    if(posL<=l&&r<=posR){
        return rmq[k];
    }
    ll ans=inf;
    if(l!=r){
        int mid=(l+r)/2;
        if(mid>=posL){
            ll tmp = query(2*k+1,l,mid,posL,posR);
            ans=min(tmp,ans);
        }
        if(mid+1<=posR){
            ll tmp=query(2*k+2,mid+1,r,posL,posR);
            ans=min(tmp,ans);
        }
    }
    return ans;
}

ll oneTo[N];//从第1列扔下 到穿过第i个漏斗 的最少花销
ll mTo[N];//从第m列扔下 .....

ll dp(int n,int m){
    fill(oneTo,oneTo+n+1,inf);
    initRmq(0,1,m);
    addNode(0,1,m,1,0);//1扔下 花销0
    for(int i=0;i<n;++i){
        ll val = query(0,1,m,pot[i][0],pot[i][1]);
        oneTo[i]=min(oneTo[i],pot[i][3]+val);
        addNode(0,1,m,pot[i][2],oneTo[i]);
    }

    fill(mTo,mTo+n+1,inf);
    initRmq(0,1,m);
    addNode(0,1,m,m,0);
    for(int i=0;i<n;++i){
        ll val = query(0,1,m,pot[i][0],pot[i][1]);
        mTo[i]=min(mTo[i],pot[i][3]+val);
        addNode(0,1,m,pot[i][2],mTo[i]);
    }

    ll ans =inf;
    for(int i=0;i<n;++i){
        ans = min(ans,oneTo[i]+mTo[i]-pot[i][3]);
    }
    return ans>=inf?-1:ans;
}

int main()
{
    //freopen("/home/lu/Documents/r.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int n=read(),m=read();
    //scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i){
        for(int j=0;j<4;++j){
            pot[i][j]=read();
        }
    }
    m=Hash(n,m);
    printf("%lld\n",dp(n,m));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值