P6544 解题报告
前言
感觉没有紫……
但是评了我也没意见。
思路分析
首先考虑怎样计算答案。
如果 \(b\) 在 \(a\) 的左侧,那么设 \(x\) 为 \([b,a]\) 的最大值,\(c\) 为 \([a,n]\) 中最靠左的 \(d_c>x\) 的位置,那么答案为 \(b-c-1\)。
应该不难理解。想要吃掉 \(b\),必须吃掉 \([b,a]\) 的所有蛋糕,和 \([a,n]\) 中部分比 \(d_b\) 小的蛋糕。
可以直接线段树二分解决。
瓶颈在于修改。
发现这个修改非常神秘,它只修改值的大小关系,但是不修改具体的值。
因为 \(e\le 10\) 非常不正常,所以考虑从这里入手解决。
发现如果我们只维护前 \(10\) 大所在的位置,那么修改时可以把前 \(e-1\) 大的值整体向前平移一段距离,给第 \(e\) 大留出位置,然后把第 \(e+1\) 大往后的排名整体后移。
感觉上比维护实数值好写多了。
然后就做完了。
实际上,只需要维护一棵支持单点修改,查询最大值,查询区间 \(>k\) 的最靠左/右的下标的线段树就行了。
总体复杂度 \(O(en\log n)\),在洛谷上跑进了 300 ms。
代码实现
感觉上不是很难写,但是从想到做完还是用了 1 h。
#include<bits/stdc++.h>
using namespace std;
int n,m,a,pos,maxn,sum,num,id,e,d[250005],c[250005];
char op;
int val[500005],ls[500005],rs[500005],dcnt,rt;
void pushup(int x){val[x]=max(val[ls[x]],val[rs[x]]);
}
void build(int l,int r,int &x){x=++dcnt;if(l==r){val[x]=d[l];return;}int mid=(l+r)>>1;build(l,mid,ls[x]);build(mid+1,r,rs[x]);pushup(x);
}
void modify(int l,int r,int pos,int k,int x){if(l==r && l==pos){val[x]=k;return;}int mid=(l+r)>>1;if(pos<=mid) modify(l,mid,pos,k,ls[x]);else modify(mid+1,r,pos,k,rs[x]);pushup(x);
}
int query(int l,int r,int ql,int qr,int x){if(ql<=l && r<=qr){return val[x];}int mid=(l+r)>>1,ans=0;if(ql<=mid) ans=max(ans,query(l,mid,ql,qr,ls[x]));if(qr>=mid+1) ans=max(ans,query(mid+1,r,ql,qr,rs[x]));return ans;
}
int find_l(int l,int r,int ql,int qr,int k,int x){if(l==r){if(val[x]<k) return -1;else return l;}int mid=(l+r)>>1;if(ql<=mid && val[ls[x]]>=k){int ans=find_l(l,mid,ql,qr,k,ls[x]);if(ans!=-1) return ans;}if(qr>=mid+1){return find_l(mid+1,r,ql,qr,k,rs[x]);}else return -1;
}
int find_r(int l,int r,int ql,int qr,int k,int x){if(l==r){if(val[x]<k) return -1;else return l;} int mid=(l+r)>>1;if(qr>=mid+1 && val[rs[x]]>=k){int ans=find_r(mid+1,r,ql,qr,k,rs[x]);if(ans!=-1) return ans;}if(ql<=mid){return find_r(l,mid,ql,qr,k,ls[x]);}else return -1;
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>a;for(int i=1;i<=n;i++){cin>>d[i];if(n-d[i]+1<=10) c[n-d[i]+1]=i;}build(1,n,rt);cin>>m;sum=n;for(int i=1;i<=m;i++){cin>>op;if(op=='F'){cin>>pos;if(pos==a){cout<<0<<'\n';}else if(pos<a){maxn=query(1,n,pos,a-1,rt);int k=(a==n?-1:find_l(1,n,a+1,n,maxn,rt));if(k==-1) k=n+1;cout<<k-pos-1<<'\n';}else{maxn=query(1,n,a+1,pos,rt);int k=(a==1?-1:find_r(1,n,1,a-1,maxn,rt));if(k==-1) k=0;cout<<pos-k-1<<'\n';}}else{int lst=min(n,10);cin>>pos>>num;for(int i=1;i<=10;i++){if(c[i]==pos) lst=i;}for(int i=lst-1;i>=num;i--){c[i+1]=c[i];}c[num]=pos;for(int i=num;i>=1;i--){sum++;modify(1,n,c[i],sum,rt);}}}return 0;
}
后记
最后要谴责 nfls 机子慢还要开小时限,使某些人的正解被卡常了。
还有我不是高二的老年选手……