#dp,贪心,KTT#洛谷 15555 [CCPC 2025 哈尔滨站] 比赛

题目传送门


分析

首先考虑 \(k=1\) 的情形,那么我肯定是贪心让下一个实力越接近当前实力加 \(D\) 越好。

不妨都取最小值,设 \(f\) 表示当前队伍能分配到的最小实力,那么 \(f_i=\max\{l_i,f_{i-1}+D\}\)

那么每个 \(f_i\) 实际上都来自某个 \(l_j+(i-j)D\),也就是 \(f_i=\max\{l_j+(i-j)D\}\leq r_i\)

也就是说任意 \(1\leq j\leq i\leq n,l_j-jD\leq r_i-iD\),也就是 \(\max\{l_j-jD\}\leq \min\{r_i-iD\}\)

相当于 \(\max\{-jD+l_j\}+\max\{iD-r_i\}\leq 0\),记前者为 \(lmax\),后者为 \(rmax\),它们都是关于 \(D\) 的一次函数,用 KTT 来维护。

问题是 \(k>1\) 怎么办,此时我们按照模 \(k\) 剩余系进行分组去复用 \(k=1\),因为通过交换将同一个剩余系的放一起不会让答案变差,所以用这个情况判断有解。


代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long lll; const lll inf=1e18;
const int N=500011; pair<lll,lll>a[N]; int n,m,T,Q,rt[5];
struct rec{lll lmax,rmax,w,rk;}q[N]; lll ans[N];
struct Segment_Tree{
	rec K[N<<1],B[N<<1]; lll bound[N<<1],lazy[N<<1]; int cnt,rk,ls[N<<1],rs[N<<1];
	void pmax(lll k1,lll b1,lll k2,lll b2,lll &k,lll &b,lll &bound){
		if (b1<b2||(b1==b2&&k1<k2)) swap(k1,k2),swap(b1,b2);
		k=k1,b=b1;
		if (k1<k2) bound=min(bound,(b1-b2)/(k2-k1));
	}
	rec pup(rec x,rec y){
		return (rec){max(x.lmax,y.lmax),max(x.rmax,y.rmax),max(max(x.w,y.w),x.lmax+y.rmax),0};
	}
	void pup(int rt){
        bound[rt]=min(bound[ls[rt]],bound[rs[rt]]);
		pmax(K[ls[rt]].lmax,B[ls[rt]].lmax,K[rs[rt]].lmax,B[rs[rt]].lmax,K[rt].lmax,B[rt].lmax,bound[rt]);
		pmax(K[ls[rt]].rmax,B[ls[rt]].rmax,K[rs[rt]].rmax,B[rs[rt]].rmax,K[rt].rmax,B[rt].rmax,bound[rt]);
		pmax(K[ls[rt]].w,B[ls[rt]].w,K[rs[rt]].w,B[rs[rt]].w,K[rt].w,B[rt].w,bound[rt]);
		pmax(K[rt].w,B[rt].w,K[ls[rt]].lmax+K[rs[rt]].rmax,B[ls[rt]].lmax+B[rs[rt]].rmax,K[rt].w,B[rt].w,bound[rt]);
	}
	void ptag(int rt,lll z){
		B[rt].lmax+=K[rt].lmax*z,
		B[rt].rmax+=K[rt].rmax*z,
        B[rt].w+=K[rt].w*z,lazy[rt]+=z;
		if (bound[rt]!=inf) bound[rt]-=z;
	}
	void build(int &rt,int l,int r){
		lazy[rt=++cnt]=0;
		if (l==r){
			K[rt]=(rec){-l,l,0,0},B[rt]=(rec){a[l*m+rk].first,-a[l*m+rk].second,0,0},bound[rt]=inf;
			return;
		}
		int mid=(l+r)>>1;
		build(ls[rt],l,mid);
		build(rs[rt],mid+1,r);
		pup(rt);
	}
	void rebuild(int rt,int l,int r,lll z){
		if (z<=bound[rt]){
			ptag(rt,z);
			return;
		}
		int mid=(l+r)>>1;
		if (lazy[rt]){
			ptag(ls[rt],lazy[rt]);
			ptag(rs[rt],lazy[rt]);
			lazy[rt]=0;
		}
		rebuild(ls[rt],l,mid,z);
		rebuild(rs[rt],mid+1,r,z);
		pup(rt);
	}
	rec query(int rt,int l,int r,int x,int y){
		if (l==x&&r==y) return B[rt];
		int mid=(l+r)>>1;
		if (lazy[rt]){
			ptag(ls[rt],lazy[rt]);
			ptag(rs[rt],lazy[rt]);
			lazy[rt]=0;
		}
		if (y<=mid) return query(ls[rt],l,mid,x,y);
		    else if (x>mid) return query(rs[rt],mid+1,r,x,y);
		        else return pup(query(ls[rt],l,mid,x,mid),query(rs[rt],mid+1,r,mid+1,y));
	}
}Tre[5];
bool cmp(rec x,rec y){return x.w<y.w;}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
    for (int i=0;i<5;++i) Tre[i].rk=i;
    for (cin>>T;T;--T){
        cin>>n>>m;
        for (int i=0;i<n;++i) cin>>a[i].first>>a[i].second;
        cin>>Q;
        for (int i=1;i<=Q;++i) cin>>q[i].lmax>>q[i].rmax>>q[i].w,q[i].rk=i,--q[i].lmax,--q[i].rmax;
        sort(q+1,q+1+Q,cmp),q[0].w=q[1].w;
        for (int i=0;i<n;++i) a[i].first-=i/m*q[0].w,a[i].second-=i/m*q[0].w;
        for (int i=0;i<m;++i) Tre[i].build(rt[i]=Tre[i].cnt=0,0,n/m-(n%m<i));
        for (int l=1,r;l<=Q;l=r+1){
            for (r=l;r<=Q&&q[r].w==q[l].w;++r); --r;
            if (q[l].w!=q[l-1].w) for (int i=0;i<m;++i)
                Tre[i].rebuild(rt[i],0,n/m-(n%m<i),q[l].w-q[l-1].w);
            for (int i=l;i<=r;++i){
                lll now=0;
                for (int j=0;j<m;++j){
                    int L=q[i].lmax/m-(q[i].lmax%m<=j)+1,R=q[i].rmax/m-(q[i].rmax%m<j);
                    now=max(now,Tre[j].query(rt[j],0,n/m-(n%m<j),L,R).w);
                }
                ans[q[i].rk]=now;
            }
        }
        for (int i=1;i<=Q;++i)
        if (!ans[i]) cout<<"YES\n";
            else cout<<"NO\n";
    }
	return 0;
}
posted @ 2026-10-03 01:29  lemondinosaur  阅读(12)  评论(0)    收藏  举报