bzoj 4700 适者

李超线段树.

  • 如果没有秒杀,就是个简单的贪心.算出第 $i$ 个敌人需要攻击的次数 $T_i$ ,考虑在攻击顺序中,交换两个相邻敌人带来的影响.
  • 容易发现按照 $\frac {T_i} {A_i}$ 从小到大排序就可以了.
  • 现在可以秒杀两个敌人,先按照 $\frac {T_i} {A_i}$ 从小到大排个序.但肯定不能贪心秒杀前两个.因为原来是考虑了击杀时间,而秒杀没有击杀时间.可以随意举出反例.
  • 考虑秒杀 $i,j(i<j)$ 时,答案会减少的值.记 $preT,sufA$ 分别表示 $T$ 的前缀和, $A$ 的后缀和.贡献就有 $i,j,[i+1,j-1],[j+1,n]$ 这四段.
  • 发现如果固定 $i$ ,那么就是要求 $-A_j\cdot T_i+b_j$ 的最大值,其中 $b_j$ 是可以预处理的,是仅和 $j$ 有关的一个量.
  • 那么就是求 $x=T_i$ 这条直线与 $j>i$ 的这些直线 $(-A_j,b_j)$ 交点纵坐标最大值.枚举 $i$ 时从大到小,就只需要维护加入直线和询问这两个操作.用李超线段树维护一下就可以了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int MAXN=3e5+10;
int n,ATK;
ll k[MAXN],b[MAXN],ans=0,delta=0;
ll calc(int seg,int pos)
{
return 1LL * k[seg] * pos + b[seg];
}
bool is_cover(int a,int b,int pos)
{
return calc(a,pos)>=calc(b,pos);
}
struct Segtree
{
int v[MAXN<<2];
void ins(int o,int l,int r,int c)
{
if(l==r)
{
if(is_cover(c,v[o],l))
v[o]=c;
return;
}
int mid=(l+r)>>1;
if(k[c]>k[v[o]])
{
if(is_cover(c,v[o],mid))
ins(o<<1,l,mid,c),v[o]=c;
else
ins(o<<1|1,mid+1,r,c);
}
else
{
if(is_cover(c,v[o],mid))
ins(o<<1|1,mid+1,r,c),v[o]=c;
else
ins(o<<1,l,mid,c);
}
}
ll query(int o,int l,int r,int pos)
{
ll res=calc(v[o],pos);
if(l==r)
return res;
int mid=(l+r)>>1;
if(pos<=mid)
res=max(res,query(o<<1,l,mid,pos));
else
res=max(res,query(o<<1|1,mid+1,r,pos));
return res;
}
}T;
struct enemy
{
int a,t;
bool operator < (const enemy &rhs) const
{
return a*rhs.t>rhs.a*t;
}
}p[MAXN];
ll preT[MAXN],sufA[MAXN];
int main()
{
n=read(),ATK=read();
for(int i=1;i<=n;++i)
{
p[i].a=read();
int D=read();
p[i].t=(D+ATK-1)/ATK;
}
sort(p+1,p+1+n);
for(int i=1;i<=n;++i)
preT[i]=preT[i-1]+p[i].t;
for(int i=n;i>=1;--i)
sufA[i]=sufA[i+1]+p[i].a;
for(int i=1;i<=n;++i)
{
k[i]=-p[i].a;
b[i]=sufA[i]*p[i].t+preT[i-1]*p[i].a-p[i].a;
ans+=p[i].t*sufA[i]-p[i].a;
}
T.ins(1,1,n,n);
for(int i=n-1;i>=1;--i)
{
delta=max(delta,T.query(1,1,n,p[i].t)+b[i]);
T.ins(1,1,n,i);
}
ans-=delta;
cout<<ans<<endl;
return 0;
}