Solution
先讲讲暴力怎么做。
显然可以费用流。设
p
=
m
a
x
{
p
i
}
p=max\{p_i\}
p=max{pi},建
p
p
p个点表示
p
p
p天,对于一种蔬菜
i
i
i,设
k
=
min
(
p
,
⌈
c
i
x
i
⌉
)
k=\min(p,\lceil {c_i\over x_i}\rceil)
k=min(p,⌈xici⌉),对于这种蔬菜,源点向前
k
−
1
k-1
k−1个点(前
k
−
1
k-1
k−1天)连流量为
x
i
x_i
xi,费用为
a
i
a_i
ai的边,向最后一天的点连流量为
c
i
−
(
k
−
1
)
x
i
−
1
c_i-(k-1)x_i-1
ci−(k−1)xi−1,费用为
a
i
a_i
ai的边和流量为
1
1
1,费用为
a
i
+
s
i
a_i+s_i
ai+si的边,然后对于每一天的点,向前一天的点连流量为
i
n
f
inf
inf,费用为
0
0
0的边,再向汇点连流量为
m
m
m,费用为
0
0
0的边。这样可以保证每天的可用蔬菜都是合法的。因为是多组询问,可以这样:询问从小到大排序,每次增加一些流量继续跑,在LOJ上获得68分的好成绩。
考虑优化。注意到第
i
i
i天选择的蔬菜肯定是第
i
+
1
i+1
i+1天选择蔬菜的子集,所以这个费用流实际上是没有退流的,所以可以模拟费用流。考虑用一个堆维护当前可用的蔬菜,那么当前的这种蔬菜肯定是选择最晚过期的那一批流到对应的那天,感性地理解一下,能够流到前面几天的更多,所以肯定是流向尽量后,用一个堆+并查集维护就行了。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010,N=100000;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,k,f[Maxn],cnt[Maxn];LL ans=0,Ans[Maxn];
int ff(int x){return((f[x]==x)?x:f[x]=ff(f[x]));}
struct P{int a,s,c,x;}p[Maxn];
struct Q{int x,id;}q[Maxn];
bool cmp(Q a,Q b){return a.x<b.x;}
struct Node
{
int v,id;
Node(int _v,int _id){v=_v,id=_id;}
};
bool operator<(Node a,Node b){return a.v<b.v;}
priority_queue<Node>que;
int main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
{
p[i].a=read(),p[i].s=read(),p[i].c=read(),p[i].x=read();
que.push(Node(p[i].a+p[i].s,i));
}
for(int i=0;i<=N;i++)f[i]=i,cnt[i]=0;
for(int i=1;i<=N;i++)
{
int tmp=m;
while(tmp&&!que.empty())
{
Node t=que.top();que.pop();
int pos;
if(!p[t.id].x)pos=N;else pos=min(p[t.id].c/p[t.id].x+(p[t.id].c%p[t.id].x!=0),N);
int Pos=ff(pos);if(!Pos)continue;
cnt[Pos]++;if(cnt[Pos]==m)f[Pos]=ff(Pos-1);
ans+=t.v;p[t.id].c--;if(p[t.id].c)que.push(Node(p[t.id].a,t.id));
tmp--;
}
Ans[i]=ans;
}
while(k--)printf("%lld\n",Ans[read()]);
}