[SDOI2013]保护出题人
题目
补充一个数据范围:
对于 \(30\%\) 的数据,保证 \(n\le 10^3\);
对于 \(50\%\) 的数据,保证 \(n\le 10^4\);
对于 \(70\%\) 的数据,保证 \(n\le 10^5,1\le d,x,a \le 10^6\);
对于所有数据,保证 \(n\le 10^6,1\le d,x,a\le 10^{12}\);
题解
明白题意——不存在伤害溢出,即豌豆射手可以在一秒钟内,前 \(0.3s\) 攻击前一个僵尸,后 \(0.7s\) 攻击另外一个僵尸。
形象地说,这是高级豌豆射手了,发射的是激光......
先分析如何拿到部分分:
对于前一组数据
由于是 \(n\le 10^3\),我们可以考虑一下暴力,首先 \(n\) 的枚举是不可或缺的,而后,对于每一组的答案来说,单调性是显然的,那么我们可以考虑二分一个 \(x\),用 \(n\) 来检查这个 \(x\) 的合法性,总复杂度在 \(\mathcal O(n^2\log a)\).
对于前两组数据
往深挖掘一下题目性质,我们发现这个二分是根本没有必要的。
对于第 \(i\) 组的第 \(j\) 只僵尸(显然有 \(j\le i\)),显然要打倒它需要先将其前面的所有僵尸以及它自己的血量减到 \(0\),而它需要走过的距离为 \(x_i+d\times (i-j+1)\),由于我们要使攻击力最小,那么就需要它刚刚好在走到门口的时候将其打死,那么一只僵尸的最低要求即为
考虑用前缀和来简化柿子,记 \(pre_t=\sum_{i=1}^ta_i\),而我们要让每只僵尸都满足要求,那么我 们要取所有僵尸中的最大值,那么对于每一组,实际上我们要求的是
可以发现这样做其实是 \(\mathcal O(n^2)\) 的,刚好过得去 \(n\le 10^4\) 的数据。
对于正解
继续分析柿子,将柿子变换一下
发现一些相同的地方,比如 \(i\) 和 \(j-1\),这个时候要斜率联系起来了,将柿子中的两个部分分别看成两个点 \((x_i+d\times i,pre_i)\) 与 \((pre_{j-1},d\times (j-1))\),那么这个柿子其实就是这俩点连线的斜率,最后我们要求的其实就是斜率的最大值。
发现后面一个点,也就是 \((pre_{j-1},d\times (j-1))\) 与 \(i\) 无关,即它在组数变化时独立,只和僵尸本身的信息有关,那么我们可以考虑将所有僵尸保存在一个数据结构中,每次移动一个 \(i\) 时,就是加入一个僵尸的信息(其信息为一个点 \((pre_{i-1},d\times (i-1))\)),也相当于在一个点集中加入一个点,而询问,就是在所有僵尸的点构成的一个点集中询问定点与点集中的斜率最大值。
考虑使用凸包维护点集,应该维护其上凸性,下面给出证明:
假设存在这样的情况:
可以看出,这是一个下凸包,和我们所说的相反,现在我们要证明,点 \(2\) 是不可能成为答案的,对于我们的目标点(也就是点 \((pre_{j-1},d\times (j-1))\))我们分开讨论它在凸包下与凸包上
- 目标点在凸包下方,那么它和每个点的连线是这个样子:
可以看出,\(i-1\) 这条线的斜率大于 \(i-2\);
- 目标点在凸包上方,连线是这个样子
显然 \(i-3\) 斜率比 \(i-2\) 大;
两种情况 \(2\) 都不可能成为答案,那么加点时我们维护一个上凸包即可。
对于寻找斜率最大值,显然可以用二分来做(机房大佬们人均三分,但是我似乎有什么误解),二分寻找斜率最大值即可。
由于每个僵尸只会加进去一次,也只会出来一次,所以均摊是 \(\mathcal O(1)\) 的,主要还是询问的二分,最终复杂度为 \(\mathcal O(n\log n)\).
代码
#include<cstdio>
#include<utility>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int maxn=1e5;
LL a[maxn+5],x[maxn+5],pre[maxn+5],d;
int n;
inline void Init(){
n=read(1),d=read(1ll);
rep(i,1,n)a[i]=read(1ll),x[i]=read(1ll),pre[i]=pre[i-1]+a[i];
}
double ans;
typedef pair<LL,LL> point;
point stk[maxn+5];int st;
inline double slope(const point a,const point b){
return 1.0*(a.sd-b.sd)/(a.ft-b.ft);
}
signed main(){
Init();
double ans=0;
point add,now;
rep(i,1,n){
add=mp(d*i,pre[i-1]),now=mp(x[i]+d*i,pre[i]);
while(st>1 && slope(add,stk[st])<slope(stk[st],stk[st-1]))--st;
stk[++st]=add;
// printf("add == (%lld, %lld)\n",add.ft,add.sd);
int l=1,r=st,mid,ret;
// printf("Now i == %d, l == %d, r == %d\n",i,l,r);
while(l<=r){
mid=l+r>>1;
if(slope(now,stk[mid])>slope(now,stk[mid-1]))l=mid+1,ret=mid;
else r=mid-1;
}
// printf("element ret : (%lld, %lld)\n",stk[ret].ft,stk[ret].sd);
// printf("ret == %d, %.6f\n",ret,slope(now,stk[ret]));
ans+=slope(now,stk[ret]);
}printf("%lld\n",(LL)(ans+0.5));
return 0;
}