2025 1.8 做题记录

CF323C

注意到这是两个排列。考虑对于每个值 x,记录 a,b 表示其在第一个和第二个排列中的位置。那么 x 会被算进答案当且仅当 l1ar1l2br2。那么这就相当于查询一个矩形内散点的数量。直接主席树维护即可。时间复杂度 O((n+m)logn)

CF1370D

典 trick。对于找最小的值,我们可以考虑二分答案。将所有的 aimid 的点视为 1,其余视为 0。那么原问题就转化为:求是否存在一个长度为 k 的子序列,使得其奇数位全为 1 或偶数为全为 1。我们分开来讨论。拿前者举例,记 fi,0/1 表示前 i 个数,当最后一位是偶数位或奇数位时能够凑出的最大长度,且满足选出的奇数位全为 1。那么有:fi,0=max(fi1,0,fi1,1+1),fi,1=max(fi1,1,(fi1,0+1)[ai=1])。偶数位全为 1 的情况类似,则只需要满足这两种情况中存在一组 max(fn,0,fn,1)k 就符合条件。时间复杂度 O(nlogn)

CF1404C

考虑观察什么样的点会被加到答案里面。对于 ai,如果 ai>i,由于我们删其它数只可能让它往前移动,所以不可能删掉它。如果 aii,因为删掉 aj(j>i)ai 的位置没有影响,而将 ai 移动到 jai 至少需要 iai 步,所以当 i 之前的数能够被删除不少于 iai 个时可以删掉 ai。则能够得到暴力:

il void solve(){
	n=rd,q=rd;
	for(re int i=1;i<=n;++i){
		a[i]=rd;
		if(a[i]<=i) b[++len]={i,i-a[i]};
	}
	while(q--){
		int x=rd,y=n-rd+1;
		++x,--y;
		int l=lower_bound(b+1,b+len+1,pii{x,-1})-b;
		int r=upper_bound(b+1,b+len+1,pii{y,inf})-b-1;
		int c=0;
		for(re int i=l;i<=r;++i) c+=(c>=b[i].y);
		cout<<c<<"\n";
	}
    return ;
}

考虑优化。注意到这个形式貌似很典,考虑分块维护。我们记 gi 为第 i 个数能被删时,i 所在的块之前至少需要删多少个数。则有:gi+j=lii1[gjgi]iai。这个可以通过二分然后主席树维护做到 O(mlog2m)。其中 m 是实际可能被删的点数。但是暴力求 j=lii1[gjgi] 也是对的,估计是数据水了。

那么对于散块,我们暴力维护删了多少个点,记 sumi,j 为第 i 个块中,gkj 的数量。则有:cntcnt+sumi,cnt。这样直接做的时间复杂度是 O(mm+mlog2m),但是空间复杂度达到了 O(mm+mlogm),难以接受。

这里又有个很典的 trick,考虑将询问离线,那么我们去枚举每一个块。记 sumi 为这个块中 gki 的数量。再去枚举每一个包含这个块的询问,将其答案直接累加,散块的问题额外处理。就能将空间降到 O(m+mlogm) 了,时间复杂度不变。

这里给出暴力求 gi 的代码,有点小慢。

#define id(x) ((x-1)/len+1)
const int N=3e5+1,M=sqrt(N)+1;
int n,q,b[N],c[N],m;
int len,g[N];
int mx[M],C[N];
int ans[N];
struct node{
	int id,R,r;
};
int le[N],ri[N],rr[N];

il void query(int l,int r,int id){
	int bl=id(l),br=id(r);
	if(bl==br){
		le[id]=-1;
		for(re int i=l;i<=r;++i) ans[id]+=(ans[id]>=c[i]);
		return ;
	}
	for(re int i=l;i<=bl*len;++i) ans[id]+=(ans[id]>=c[i]);
	if(bl+1>=br){
		le[id]=-1;
		for(re int i=(br-1)*len+1;i<=r;++i) ans[id]+=(ans[id]>=c[i]);
		return ;
	}
	le[id]=bl+1,ri[id]=br,rr[id]=r;
	return ;
}
il int get(int l,int r,int x){
	int cnt=0;
	for(re int i=l;i<=r;++i) cnt+=(g[i]<=x);
	return cnt;
}

il void solve(){
	n=rd,q=rd;
	for(re int i=1;i<=n;++i){
		int x=rd;
		if(x<=i&&i-x<=i-1) b[++m]=i,c[m]=i-x;
	}
	len=sqrt(m);
	for(re int i=1;i<=m;++i){
		int l=0,r=n,ans=-1;
		while(l<=r){
			int mid=l+r>>1;
			if(get((id(i)-1)*len+1,i-1,mid)+mid>=c[i]) ans=mid,r=mid-1;
			else l=mid+1;
		}
		g[i]=ans,mx[id(i)]=max(mx[id(i)],g[i]);
	}
	for(re int i=1;i<=q;++i){
		int x=rd,y=n-rd+1;
		++x,--y;
		int l=lower_bound(b+1,b+m+1,x)-b;
		int r=upper_bound(b+1,b+m+1,y)-b-1;
		query(l,r,i);
	}
	for(re int i=1;i<=id(m);++i){
		for(re int j=0;j<=mx[i];++j) C[j]=0;
		for(re int j=(i-1)*len+1;j<=min(i*len,m);++j) ++C[g[j]];
		for(re int j=1;j<=mx[i];++j) C[j]+=C[j-1];
		for(re int id=1;id<=q;++id){
			if(le[id]==-1) continue;
			if(ri[id]<i||le[id]>i) continue;
			if(ri[id]==i){
				for(re int j=(i-1)*len+1;j<=rr[id];++j) ans[id]+=(ans[id]>=c[j]);
				continue;
			}
			ans[id]+=C[min(mx[i],ans[id])];
		}
	}
	for(re int i=1;i<=q;++i) printf("%d\n",ans[i]);
    return ;
}
posted @   harmis_yz  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示