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