CSP-J2025题解

赛时挂爆了……

这篇题解不讲部分分

T1 拼数

没什么多说的,挺水的
纯暴力+桶排序(我当时顺手用了sort)
代码如下

#include<bits/stdc++.h>
using namespace std;

string s;
int a[1000001],cnt=0;

int main(){
    cin.tie(0)->sync_with_stdio(0);
    cin>>s;
    int n=s.size();
    for(int i=0;i<n;i++){
        if('0'<=s[i]&&s[i]<='9'){
            a[++cnt]=s[i]-'0';
        }
    }
    sort(a+1,a+cnt+1);
    for(int i=cnt;i;i--){
        cout<<a[i];
    }
}

T2 座位

wasser!
可以数学,也可以大模拟。(n,m只有10跟玩没区别)

代码如下

#include<bits/stdc++.h>
using namespace std;

int n,m,ans[15][15];
struct info{
    int id,v;
}a[105];

bool cmp(info A,info B){
    return A.v>B.v;
}

int main(){
    cin.tie(0)->sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n*m;i++){
        cin>>a[i].v;
        a[i].id=i;
    }
    sort(a+1,a+n*m+1,cmp);
    int x=1,y=1;
    bool dir=0;//0 down 1 up
    for(int t=1;t<=n*m;t++){
        ans[x][y]=a[t].id;
        int nx=x,ny=y;
        if(!dir) nx++;
        else nx--;
        if(nx<1){
            ny++;
            dir=!dir;
            nx=1;
        }else if(nx>n){
            ny++;
            dir=!dir;
            nx=n;
        }
        x=nx,y=ny;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(ans[i][j]==1){
                cout<<j<<' '<<i<<endl;
            }
        }
    }
    return 0;
}

T3 异或和

说实话这个题目想复杂了
首先,想求出一段异或和,不难想到前缀异或和。
我们可以注意到一个异或的特性
如果\(s_r⊕s_{l-1}=k\)
那么\(s_r⊕k=s_{l-1}\)
有了这个结论轻松多了。不难想到双指针。记lst为上一个区间的右端点,防止重叠。循环枚举右端点。设当前枚举到i,如果有\(s_x=s_i⊕k\),那么就把两lst更新为i,答案再加一。当然还要及一个pos数组i表示前缀和数组中结果为i的最靠右的位置在哪。每轮更新即可

代码如下

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=500005;
int n,k,a[N],s[N];
int pos[N*10];

signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) s[i]=s[i-1]^a[i];
	memset(pos,-1,sizeof pos);
	pos[0]=0;
	int ans=0;int lst=0;
	for(int i=1;i<=n;i++){
		int x=s[i]^k;
		if(pos[x]>=lst){
			ans++;
			lst=i;
		}
		pos[s[i]]=i;
	}
	cout<<ans<<endl;
}

T4 多边形

这个题目不难想到背包。
首先要排序,这样就不用管最大值了。
我们让\(dp_{i,j}\)表示前i个里面选出j个棍的方案数。发现边界条件是对于所有的i都有\(dp_{i,0}=1\)
转移方程如下
无论如何,都先有\(dp_{i,j}=dp_{i-1,j}\)
\(j≥a_i\)是, \(dp_{i,j}=max(dp_{i,j},dp_{i-1,j-a_i})\)
然后统计答案,枚举i表示选出边得数量,然后统计有多少个和是大于当当前的值。那么这个做法爆空间。
我们发现,正难则反。如果求合法的范围太大(5000³),那么我们可以反着枚举不合法的。拿总共方案数得到最后的答案。这个时候就是一个组合数问题,写一个杨辉三角即可

代码如下

#include<bits/stdc++.h>
using namespace std;

const int N=5005,mod=998244353;
int n,a[N],dp[N][N],c[N][N];

void init(){
	c[1][1]=1;
	for(int i=2;i<=5002;i++)//注意,一定要多算1个,保险5个!
		for(int j=1;j<=i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}

int main(){
	cin.tie(0)->sync_with_stdio(0);
	cin>>n;
	init();
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=0;i<=n;i++) dp[i][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=5000;j++){
			dp[i][j]=dp[i-1][j];
			if(j>=a[i]) dp[i][j]=(dp[i][j]+dp[i-1][j-a[i]])%mod;
		}
	int ans=0;
	for(int i=3;i<=n;i++){
		for(int j=1;j<=i-1;j++) ans=(ans+c[i][j+1])%mod;
		for(int j=1;j<=a[i];j++) ans=(ans-dp[i-1][j]+mod)%mod;
	}
	cout<<ans<<endl;
}
posted @ 2026-01-24 11:26  zhangruotian_Max  阅读(2)  评论(0)    收藏  举报