2025 1.8 做题记录

CF323C

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

CF1370D

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

CF1404C

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

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

考虑优化。注意到这个形式貌似很典,考虑分块维护。我们记 \(g_i\) 为第 \(i\) 个数能被删时,\(i\) 所在的块之前至少需要删多少个数。则有:\(g_i+\sum\limits_{j=l_i}^{i-1}[g_j \le g_i] \ge i-a_i\)。这个可以通过二分然后主席树维护做到 \(O(m\log^2 m)\)。其中 \(m\) 是实际可能被删的点数。但是暴力求 \(\sum\limits_{j=l_i}^{i-1}[g_j \le g_i]\) 也是对的,估计是数据水了。

那么对于散块,我们暴力维护删了多少个点,记 \(sum_{i,j}\) 为第 \(i\) 个块中,\(g_k \le j\) 的数量。则有:\(cnt \to cnt+sum_{i,cnt}\)。这样直接做的时间复杂度是 \(O(m\sqrt{m}+m\log^2 m)\),但是空间复杂度达到了 \(O(m\sqrt{m}+m\log m)\),难以接受。

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

这里给出暴力求 \(g_i\) 的代码,有点小慢。

#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 @ 2025-01-08 15:40  harmis_yz  阅读(14)  评论(0)    收藏  举报