题解:P11994 [JOIST 2025] 外郎糕 / Uiro
P11994 [JOIST 2025] 外郎糕 / Uiro
考虑怎么解决一次询问。
有一个显然的
上面那个做法不好改进。观察部分分和数据范围发现,题目引导我们对
考虑对于某个值
观察上面的性质,我们感性地猜测一个做法,从小到大枚举
这是正确的,不妨考虑如果某一层
于是经过上面的调整可以证明,最优解一定能通过这种贪心得到。直接做,枚举
代码:
#include<bits/stdc++.h>
using namespace std;
int n,Q,a[200003],q[200003][3],num[200003],pre[200003],lmt[200003],plsv[200003],lft,rgt,mid,f[200003];
int stk[200003],tots,k1,k2,k3,k4,k5,k6,k7,k8,k9,lg[200003],ST[200003][19],ST2[200003][19];
int Query(int ql,int qr){if(ql>qr)return 1000000000;return min(ST[ql][lg[qr-ql+1]],ST[qr-(1<<lg[qr-ql+1])+1][lg[qr-ql+1]]);}
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read(){
int xx=0,ff=1;
char ch=nc();
while(ch<48||ch>57)ch=nc();
while(ch>=48&&ch<=57)xx=xx*10+ch-48,ch=nc();
return xx*ff;
}
void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
return;
}
int main(){
for(int i=2;i<=200000;i++)lg[i]=lg[i>>1]+1;
n=read();
for(int i=1;i<=n;i++)a[i]=read();
Q=read();
for(int i=1;i<=Q;i++){q[i][0]=read();q[i][1]=read();}
for(int i=1;i<=Q;i++)lmt[i]=q[i][0];
for(int nowv=1;nowv<=100;nowv++){
tots=0;
for(int i=1;i<=n;i++){
num[i]=num[i-1]+(a[i]==nowv);
if(a[i]==nowv)stk[++tots]=i;
pre[i]=pre[i-1]+a[i];
if(a[i]<nowv)pre[i]-=a[i]*2;
}
if(tots==0)continue;
for(int i=1;i<=n;i++){
f[i]=pre[i]-2*nowv*num[i];
if(a[i]!=nowv)f[i]=min(f[i],f[i-1]);
}
for(int i=1;i<=tots;i++)ST[i][0]=f[stk[i+1]-1];
for(int i=1;(1<<i)<=tots;i++){
for(int j=1;j+(1<<i)-1<=tots;j++)ST[j][i]=min(ST[j][i-1],ST[j+(1<<(i-1))][i-1]);
}
stk[tots+1]=n+1;
for(int i=1;i<=Q;i++){
lft=num[lmt[i]-1]+1;rgt=num[q[i][1]]+1;
while(lft<rgt){
mid=((lft+rgt)/2);
if(Query(mid,num[q[i][1]]-1)>=pre[q[i][0]-1]-plsv[i]-2*nowv*(mid-1)&&f[q[i][1]]>=pre[q[i][0]-1]-plsv[i]-2*nowv*(mid-1))rgt=mid;
else lft=mid+1;
}
q[i][2]+=(num[q[i][1]]-lft+1);
lmt[i]=max(lmt[i],stk[lft-1]+1);
plsv[i]+=((lft-1)-num[q[i][0]-1])*nowv*2;
}
}
for(int i=1;i<=Q;i++){write(q[i][2]);putchar('\n');}
return 0;
}