数据结构阶段测试

link

A

预计:100,实际:100。

平衡树维护之即可。

实现
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=4e5+5;
int m,q,root,tot;
struct TREE{
	int ls,rs,siz,rnk,val;
}tree[N];

int addnode(int val){
	tree[++tot].val=val;
	tree[tot].siz=1;
	tree[tot].rnk=rand();
	tree[tot].ls=tree[tot].rs=0;
	return tot;
}
void pushup(int k){
	tree[k].siz=tree[tree[k].ls].siz+tree[tree[k].rs].siz+1;
}
void split(int k,int &a,int &b,int val){
	if(!k){
		a=b=0;
		return;
	}
	if(tree[k].val<=val)
		a=k,split(tree[k].rs,tree[k].rs,b,val);
	else
		b=k,split(tree[k].ls,a,tree[k].ls,val);
	pushup(k); 
}
void merge(int &k,int a,int b){
	if(!a||!b){
		k=a+b;
		return;
	}
	if(tree[a].rnk<tree[b].rnk)
		k=a,merge(tree[k].rs,tree[k].rs,b);
	else
		k=b,merge(tree[k].ls,a,tree[k].ls);
	pushup(k);
}
void ins(int &k,int val){
	int a=0,b=0,cur=addnode(val);
	split(k,a,b,val);
	merge(a,a,cur);
	merge(k,a,b);
}
int fndrnk(int k,int x){
	while(tree[tree[k].ls].siz+1!=x){
		if(tree[tree[k].ls].siz>=x)
			k=tree[k].ls;
		else
			x-=tree[tree[k].ls].siz+1,k=tree[k].rs;
	}
	return tree[k].val;
}

signed main(){
	//freopen("A.in","r",stdin);
	//freopen("A.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	srand(time(0));
	cin>>m>>q;
	for(int i=1,x;i<=m;i++)
		cin>>x,ins(root,x);
	while(q--){
		int op,x;
		cin>>op>>x;
		if(op==1)
			cout<<fndrnk(root,tree[root].siz-x+1)<<'\n';
		else
			ins(root,x);
	}
	return 0;
}

B

预计:30,实际:88。

经典题,赛时只想到了暴力跳。

其实值域分块一下,每次按块跳,跳到有空位的块(这个,另开一个桶维护之)就暴力查询即可。

实现
//
//  P4137.cpp
//  
//
//  Created by _XOFqwq on 2025/4/26.
//

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std;

const int N=2e5+5;
int n,m,t,tt;
int a[N],c[N],cnt[N],ans[N];
int L[N],R[N],pos[N];
struct Q {
    int l,r,id;
}q[N];

bool cmp(Q &x,Q &y){
    return (x.l/tt!=y.l/tt?x.l<y.l:((x.l/tt)&1?x.r<y.r:x.r>y.r));
}
void add(int x){
    if (!c[a[x]]) {
        cnt[pos[a[x]]]++;
    }
    c[a[x]]++;
}
void del(int x){
    c[a[x]]--;
    if (!c[a[x]]) {
        cnt[pos[a[x]]]--;
    }
}

signed main(){
    //freopen("P4137_1.in","r",stdin);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m;
    for (int i=1; i<=n; i++) {
        cin>>a[i];
    }
    for (int i=1; i<=m; i++) {
        cin>>q[i].l>>q[i].r,q[i].id=i;
    }
    tt=sqrt(n);
    sort(q+1,q+m+1,cmp);
    t=sqrt(N);
    L[1]=0,R[1]=sqrt(N)-1;
    for (int i=2; i<=t; i++) {
        L[i]=R[i-1]+1,R[i]=L[i]+sqrt(N)-1;
    }
    if (R[t]<N) {
        t++,L[t]=R[t-1]+1,R[t]=N;
    }
    for (int i=1; i<=t; i++) {
        for (int j=L[i]; j<=R[i]; j++) {
            pos[j]=i;
        }
    }
    int x=1,y=0;
    for (int i=1; i<=m; i++) {
        int qx=q[i].l,qy=q[i].r;
        for (; x>qx; add(--x)) {}
        for (; y<qy; add(++y)) {}
        for (; x<qx; del(x++)) {}
        for (; y>qy; del(y--)) {}
        int tot=0;
        for (int j=1; j<=t; j++) {
            if (cnt[j]<R[j]-L[j]+1) {
                for (int k=L[j]; k<=R[j]; k++) {
                    if (!c[k]) {
                        tot=k; break;
                    }
                }
                break;
            }
        }
        ans[q[i].id]=tot;
    }
    for (int i=1; i<=m; i++) {
        cout<<ans[i]<<'\n';
    }
    return 0;
}

总结:把每个 idea 记录下来,进行深度思考。

C

预计:10,实际:10。

只有询问,考虑莫队。

对于一个可重排为回文串的串,它必须满足出现奇数次的字母的个数不超过 \(1\),这样子是可以暴力统计的。赛时止步于此。

其实看到字符集就想到了状压,将每个字母的出现次数压成一个二进制数。

具体地,我们令 \(a_i\) 表示第 \(i\) 个字母的二进制值,则 \(a_i=2^{s_i}\)

看到要统计子区间个数,于是想到运用 CF 某题(具体忘了,反正是有次的作业题)的套路:维护前缀异或值 \(sum_i\),枚举合法子区间状态,得到另一个端点的合法状态,然后统计合法的另一个端点个数(桶维护之)即可。

实现
//
//  P3604.cpp
//  
//
//  Created by _XOFqwq on 2025/4/26.
//

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std;

const int N=1e5+5,M=(1<<26)+5;
int n,m,tot,t;
int sum[N],cnt[M],ans[N];
struct Q {
    int l,r,id;
}q[N];

bool cmp(Q &x,Q &y){
    return (x.l/t!=y.l/t?x.l<y.l:((x.l/t)&1?x.r<y.r:x.r>y.r));
}
void add(int x){
    tot+=cnt[sum[x]];
    for (int i=0; i<26; i++) {
        tot+=cnt[sum[x]^(1<<i)];
    }
    cnt[sum[x]]++;
}
void del(int x){
    cnt[sum[x]]--;
    tot-=cnt[sum[x]];
    for (int i=0; i<26; i++) {
        tot-=cnt[sum[x]^(1<<i)];
    }
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n>>m,t=sqrt(n);
    for (int i=1; i<=n; i++) {
        char c; cin>>c;
        sum[i]=sum[i-1]^(1<<(c-'a'));
    }
    for (int i=1; i<=m; i++) {
        cin>>q[i].l>>q[i].r,q[i].l--,q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    int x=1,y=0;
    for (int i=1; i<=m; i++) {
        int qx=q[i].l,qy=q[i].r;
        for (; x>qx; add(--x)) {}
        for (; y<qy; add(++y)) {}
        for (; x<qx; del(x++)) {}
        for (; y>qy; del(y--)) {}
        ans[q[i].id]=tot;
    }
    for (int i=1; i<=m; i++) {
        cout<<ans[i]<<'\n';
    }
    return 0;
}

总结:字符集->状压、统计合法子区间个数->维护前缀异或值。


整体复盘:

成绩:\(rk \ 15 \ 198pts\)

其实都是上课讲过的套路,说明理解的不够透彻,并且深度思考不够,需要加强训练。

posted @ 2025-04-27 21:57  _KidA  阅读(14)  评论(0)    收藏  举报