#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;
}

浙公网安备 33010602011771号