题解 打怪

传送门

令击杀耗时 \(c_i = \lfloor \frac{d_i-1}{b} \rfloor+1\)
首先按 \(\frac{c_i}{a_i}\) 排序很好想
然后考虑怎么处理那个秒杀
这个我不会,并且感觉除非先想到要斜率优化否则不好想到下一步
秒杀 \(i\) 能使受到的伤害减少 \(e_i = \sum\limits_{j=1}^{i-1}c_i*a_j+ a_i*(c_i-1) + \sum\limits_{j=i+1}^{n}a_i*c_j\)
于是考虑在秒杀 \(i\) 后再秒杀 \(j\) 比秒杀 \(k\) 劣的条件(\(j,k<i,\ c_j>c_k\)
\(e_i+e_j-a_j*c_i < e_i+e_k-a_k*c_i\),化为斜率式后发现需要cdq分治
但是这样是 \(O(nlog^2n)\) 的,会T掉

  • 关于CDQ分治一类 \(nlog^2n\)\(nlogn\) 的技巧:
    复杂度瓶颈在于排序,所以尝试优化排序
    若限制条件为 \(a_i<a_j, b_i<b_j\),则整体先按 \(b\) 排序
    当分治一个区间 \([l, r]\) 时,需要将区间按 \(a\) 的大小分为左右两个区间,这个可以nth_element实现(或者如果中位数很好找的话也可以直接逆归并实现)
    考虑如何在分治左区间后仍满足 \(b\) 的有序
    发现在每个区间分治结束后左右两个区间都是按 \(b\) 排序的,所以直接归并就好了

其实这题还有李超树做法,可以对原式 \(e_i+e_j-a_j*c_i < e_i+e_k-a_k*c_i\) 直接建出斜率为 \(a_j\),截距为 \(e_j\) 的线段,在每个 \(i\) 处查最值即可

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 300010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, b;
int q[N];
ll pre[N], suf[N], ans, maxn;

struct npc{ll i, a, d, c, e, ans;}p[N], tem[N];
inline bool cmp1(npc a, npc b) {return a.i<b.i;}
inline bool cmp2(npc a, npc b) {return a.c>b.c;}
inline bool cmp3(npc a, npc b) {return a.a>b.a;}
inline bool cmp4(npc a, npc b) {return 1.0*a.c/a.a<1.0*b.c/b.a;}
inline double calc(npc& a, npc& b) {
	if (a.e==b.e) return 0.0;
	if (a.c==b.c) return a.e>b.e?1e10:-1e10;
	return (1.0*a.e-b.e)/(1.0*a.c-b.c);
}

void cdq(int l, int r) {
	if (l==r) return ;
	int mid=(l+r)>>1, ql=1, qr=0, pos1=l, pos2=mid+1;
	for (int i=l; i<=r; ++i) tem[i]=p[i];
	for (int i=l; i<=r; ++i) 
		if (tem[i].i<=mid) p[pos1++]=tem[i];
		else p[pos2++]=tem[i];
	cdq(l, mid);
	// cout<<"cdq: "<<l<<' '<<r<<endl;
	// cout<<"l: "; for (int i=l; i<=mid; ++i) cout<<p[i].a<<','<<p[i].d<<','<<p[i].c<<','<<p[i].i<<' '; cout<<"r: "; for (int i=mid+1; i<=r; ++i) cout<<p[i].a<<','<<p[i].d<<','<<p[i].c<<','<<p[i].i<<' '; cout<<endl;
	for (int i=l; i<=mid; ++i) {
		while (ql<qr && calc(p[q[qr-1]], p[q[qr]])>calc(p[q[qr]], p[i])) --qr;
		q[++qr]=i;
	}
	// cout<<"q: "; for (int i=ql; i<=qr; ++i) cout<<q[i]<<' '; cout<<endl;
	for (int i=mid+1; i<=r; ++i) {
		// cout<<"calc: "<<q[ql]<<' '<<calc(p[q[ql]], p[q[ql+1]])<<' '<<p[i].a<<endl;
		while (ql<qr && calc(p[q[ql]], p[q[ql+1]])<p[i].a) {
			// cout<<"pop: "<<q[ql]<<' '<<calc(p[q[ql]], p[q[ql+1]])<<' '<<p[i].a<<endl;
			++ql;
		}
		p[i].ans=max(p[i].ans, p[i].e+p[q[ql]].e-p[i].a*p[q[ql]].c);
	}
	cdq(mid+1, r);
	pos1=l; pos2=mid+1;
	for (int i=l; i<=r; ++i) tem[i]=p[i];
	for (int i=l; i<=r; ++i)
		if (pos1>mid) p[i]=tem[pos2++];
		else if (pos2>r) p[i]=tem[pos1++];
		else p[i]=tem[((tem[pos1].c>tem[pos2].c)?pos1:pos2)++];
}

signed main()
{
	freopen("fittest.in", "r", stdin);
	freopen("fittest.out", "w", stdout);

	n=read(); b=read();
	for (int i=1; i<=n; ++i) p[i].a=read(), p[i].d=read(), p[i].c=(p[i].d-1)/b+1;
	sort(p+1, p+n+1, cmp4);
	// cout<<"p: "; for (int i=1; i<=n; ++i) cout<<p[i].a<<','<<p[i].d<<','<<p[i].c<<' '; cout<<endl;
	for (int i=1; i<=n; ++i) p[i].i=i;
	for (int i=1; i<=n; ++i) pre[i]=pre[i-1]+p[i].c;
	for (int i=n; i; --i) suf[i]=suf[i+1]+p[i].a;
	for (int i=1; i<=n; ++i) p[i].e=p[i].a*pre[i-1]+p[i].a*(p[i].c-1)+p[i].c*suf[i+1];
	for (int i=1; i<=n; ++i) ans+=p[i].a*(pre[i-1]+p[i].c-1);
	sort(p+1, p+n+1, cmp2);
	cdq(1, n);
	for (int i=1; i<=n; ++i) maxn=max(maxn, p[i].ans);
	// cout<<"ans: "<<ans<<' '<<maxn<<endl;
	printf("%lld\n", ans-maxn);

	return 0;
}
posted @ 2021-10-08 16:36  Administrator-09  阅读(5)  评论(0编辑  收藏  举报