题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4122
题目大意:第一行n,m;随后n行代表在某年某月某日有人预定x个月饼,随后一行t,s,月饼最长保存时间和每个月饼保存一小时的费用。随后m行,在0-m的时间制作月饼需要的费用。求完成订单需要的最小成本。
Sample Input
1 10 Jan 1 2000 9 10 5 2 20 20 20 10 10 8 7 9 5 10 0 0
Sample Output
70
emmm,没想到一发入魂。。。真的是太舒服了
我们先将订单时间全部转化成时刻,这个就要用到各位的模拟技术了,要考虑字符串的处理和闰年、闰月的处理,处理如下:
int big(int x) { if (x<=7 && (x&1)) return 1; if (x>=8 && !(x&1)) return 1; return 0; } void solve(string s,int data,int year,int clock,int num,int id) { int y=year-st_year,run_year=0,pin_year; if (y) { if (y%4) run_year=y/4+1; else run_year=y/4; } pin_year=y-run_year; int clocks=pin_year*365*24+run_year*366*24; int mon; for (int i=1; i<=12; i++) if (s==ss[i]) {mon=i+1; break;} else { if (big(i)) clocks+=31*24;//大月判断 else if (i!=2) clocks+=30*24; else {//闰月判断 if (year%4) clocks+=28*24; else clocks+=29*24; } } clocks+=(data-1)*24; clocks+=clock; a[id]=node{clocks+1,num};//由于开店从0开始的,而标号从1开始,所以时间上+1 }
接下来就是维护最小花费了,实际上我们只需要维护每个点制作1个月饼的最小花费就行了,那么由于T的存在,即相当于一个滑动的区间最小花费的维护,我们很容易想到单调队列,关键是怎么维护。我们开两个队列,一个用来存储位置,一个用来存储花费。最小值的维护也就是维护一个花费递增的单调队列了,当新加入一个点的时候,我们判断,在该点制作的花费$cost[i]$和队尾的花费加上$S*(i-q[tail])$那个优,其中$i$表示当前插入点的位置,$q[tail]$表示队尾最小代价的位置,那么就是保存的单位价格*保存的时间。
其主体的维护和一些小细节如下:
for (int i=1; i<=m; i++) { if (head>tail || 1LL*qs[tail]+S*(i-q[tail])<=1LL*cost[i]) { qs[++tail]=cost[i]; q[tail]=i; while (i==a[order].clock) { ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } continue; } while (head<=tail && i-q[head]>T) head++; while (head<=tail && 1LL*qs[tail]+S*(i-q[tail])>1LL*cost[i]) tail--; qs[++tail]=cost[i]; q[tail]=i; while (i==a[order].clock) { ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } } for (int i=m+1; i<=a[n].clock; i++) { while (i-q[head]>T) head++; while (i==a[order].clock) { ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } }
那么代码也就出来了
以下是AC代码:
#include <cstdio> #include <algorithm> #include <cstring> #include <string> #include <iostream> using namespace std; typedef long long ll; const int mac=3e3+10; const int maxn=1e5+10; string ss[]={"xxx","Jan","Feb","Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov","Dec"}; struct node { int clock,num; }a[mac]; int cost[maxn],st_year=2000,q[mac],qs[mac]; int big(int x) { if (x<=7 && (x&1)) return 1; if (x>=8 && !(x&1)) return 1; return 0; } void solve(string s,int data,int year,int clock,int num,int id) { int y=year-st_year,run_year=0,pin_year; if (y) { if (y%4) run_year=y/4+1; else run_year=y/4; } pin_year=y-run_year; int clocks=pin_year*365*24+run_year*366*24; int mon; for (int i=1; i<=12; i++) if (s==ss[i]) {mon=i+1; break;} else { if (big(i)) clocks+=31*24;//大月判断 else if (i!=2) clocks+=30*24; else {//闰月判断 if (year%4) clocks+=28*24; else clocks+=29*24; } } clocks+=(data-1)*24; clocks+=clock; a[id]=node{clocks+1,num};//由于开店从0开始的,而标号从1开始,所以时间上+1 } int main(int argc, char const *argv[]) { int n,m; ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); while (cin>>n>>m){ if (!n && !m) return 0; string s; int data,year,clock,num; for (int i=1; i<=n; i++){ cin>>s>>data>>year>>clock>>num; solve(s,data,year,clock,num,i); } a[n+1]=node{0,0}; int T,S; cin>>T>>S; for (int i=1; i<=m; i++) cin>>cost[i]; int head=1,tail=0,order=1; ll ans=0; for (int i=1; i<=m; i++){ if (head>tail || 1LL*qs[tail]+S*(i-q[tail])<=1LL*cost[i]) { qs[++tail]=cost[i]; q[tail]=i; while (i==a[order].clock) { ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } continue; } while (head<=tail && i-q[head]>T) head++; while (head<=tail && 1LL*qs[tail]+S*(i-q[tail])>1LL*cost[i]) tail--; qs[++tail]=cost[i]; q[tail]=i; while (i==a[order].clock){ ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } } for (int i=m+1; i<=a[n].clock; i++){ while (i-q[head]>T) head++; while (i==a[order].clock){ ans+=1LL*(qs[head]+S*(i-q[head]))*a[order].num; order++; } } printf("%lld ",ans); } return 0; }