返回顶部

CSP模拟30

难得改完一次题,写篇题解祭一下

A.枫(P7485 「Stoi2031」枫

考场居然打了个高分暴力

我的思路:假设我们已知最后一个数,逆推,不断往该数前(或后)加了多少数,直至完成这个操作。

荣获 \(96pts\) 的好成绩 评测记录

code
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+10;
int T,q,n,m,wei;
int top,tail;
void dfs(int sum,int sub,int k,int c){
	if(sum==1){
		return;
	}
	dfs(sum-sub,(sum-sub-1)/(k+1)+1,k,c+1);
	if(c&1){
		int pl=1+(top-1)/k;
		top+=pl;
		tail+=sub-pl;
	}
	else {
		int pl=1+(tail-1)/k;
		top+=sub-pl;
		tail+=pl;
	}
}
int main(){
	scanf("%d",&T);
	for(int k=1;k<=T;k++){
		scanf("%d%d",&q,&m);
		for(int i=1;i<=q;i++){
			scanf("%d",&n);
			if(n==1||n==2){
				cout<<n<<' ';
				continue;
			}
			if(k>=n-1){
				cout<<n/2+1<<' ';
			}
			else if(k==n-2||k==n-3){
				cout<<(n+1)/2<<' ';
			}
			else {
				top=1,tail=1;
				dfs(n,(n-1)/(k+1)+1,k,1);
				cout<<top<<' ';
			}
		}
		cout<<'\n';
	}
	return 0;
}

正解

一些步骤相似,可不可以认为我敲了正解(bushi)

(fengwu大佬说) 给出了每次捡枫叶的最大枫叶数,显然是递推

我们求出当前个数第一次“挽回”的个数,求出下一状态的“思念”,插入一定的枫叶即可。(注意顺序

code
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+10;
int T,q,n,m;
int ans[M];
int main(){
	scanf("%d",&T);
	ans[1]=1;
	for(int k=1;k<=T;k++){
		scanf("%d%d",&q,&m);
		for(int i=2;i<=m;i++){
			int sum=i-(i-1)/(k+1)-1;
			int pos=sum-ans[sum]+1;
			ans[i]=pos+1+(pos-1)/k;
		}
		for(int i=1;i<=q;i++){
			scanf("%d",&n);
			cout<<ans[n]<<' ';
		}
		cout<<'\n';
	}
	return 0;
}

B.Little Elephant and Broken Sorting(Little Elephant and Broken Sorting)

概率期望都不做

规定$ f_{i,j}$表示第 \(i\) 位上的数比第 \(j\) 位上的数大的概率,n的范围很小,可以直接预处理。

每次交换,需要分类讨论:

两个位置都为交换的位置
其中一个位置为交换的位置

对于第一种情况,则 \(f_{x,y}\)\(f_{y,x}\) 的值都为 \(0.5\),原因显然。

对于第二种情况,既然有 \(0.5\) 的概率更换,就需要将交换成功的概率加上交换失败的概率即可。

code
#include<bits/stdc++.h>
using namespace std;
const int M=5010;
int n,m;
int a[M];
double ans;
double f[M][M];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			f[i][j]=(a[i]>a[j]);
		}
	}
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		f[x][y]=f[y][x]=0.5;
		for(int j=1;j<=n;j++){
			if(j!=x&&j!=y){
				f[j][x]=f[j][y]=0.5*(f[j][x]+f[j][y]);
				f[x][j]=f[y][j]=0.5*(f[x][j]+f[y][j]);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(i!=j){
				ans+=f[i][j];
			}
		}
	}
	printf("%0.9lf",ans);
	return 0;
}

C.楼房重建(P4198 楼房重建)

神奇+抽象线段树

我们需要不断的区间查询和区间修改,联想到线段树

已知当一个建筑的斜率大于前面所有建筑的斜率时,该建筑可以被看到,所以我们进行 \(update\)

合并操作是难点。

设当前区间为 \(c\) ,左区间为 \(lc\) ,右区间为 \(rc\)

首先 \(lc\) 的答案是可以直接加入 \(c\) 的答案的,原因显然。

但是 \(rc\) 的答案比较特殊,可能会被 \(lc\) 遮挡,需要分类讨论。

如果c的最大值在lc,那么rc的所有建筑一定都会被阻挡,直接返回lc的答案。
如果c的最大值在rc,我们还需要再分:
   	- 如果rc的左区间的最大值比lc的最大值大,我们直接加上rc的右区间的答案,并递归rc的左区间。
    - 否则直接递归rc的右区间即可。
code
#include<bits/stdc++.h>
#define lc c<<1
#define rc c<<1|1
using namespace std;
const int M=1e5+10;
struct abc{
	int l,r,num;
	double k;
}t[M<<2];
int n,m;
void add(int l,int r,int c){
	t[c].l=l; t[c].r=r;
	if(l==r){
		return;
	}
	int mid=(l+r)>>1;
	add(l,mid,lc);
	add(mid+1,r,rc);
}
int query(double ma,int c){
	if(t[c].k<=ma){
		return 0;
	}
	if(t[c].l==t[c].r){
		return 1;
	}
	if(t[lc].k>ma){
		return query(ma,lc)+t[c].num-t[lc].num;
	}
	else {
		return query(ma,rc);
	}
}
void update(int x,double va,int c){
	if(t[c].l==x&&t[c].r==x){
		t[c].num=1;
		t[c].k=va;
		return;
	}
	int mid=(t[c].l+t[c].r)>>1;
	if(x<=mid){
		update(x,va,lc);
	}
	else {
		update(x,va,rc);
	}
	t[c].k=max(t[lc].k,t[rc].k);
	t[c].num=t[lc].num+query(t[lc].k,rc);
}
int main(){
	scanf("%d%d",&n,&m);
	add(1,n,1);
	for(int op=1;op<=m;op++){
		int x,y;
		scanf("%d%d",&x,&y);
		update(x,(double)y/x,1);
		cout<<t[1].num<<'\n'; 
	}
	return 0;
}

D.Yes or No([AGC019F] Yes or No)

但是讲不清楚,建议出门右转,找大佬小粉兔

肯定不是不想打了

code
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
const long long M=5e6+10;
long long n,m,nn,ans;
long long f[M],g[M];
long long po(long long x,long long k){
	long long ans=1;
	while(k){
		if(k&1){
			ans=ans*x%mod;
		}
		x=x*x%mod;
		k=k>>1;
	}
	return ans;
}
void pre(){
	f[0]=g[0]=1;
	for(long long i=1;i<=(n<<1);i++){
		f[i]=f[i-1]*i%mod;
		g[i]=po(f[i],mod-2);
	}
}
long long C(long long n,long long m){
	return f[n]*g[m]%mod*g[n-m]%mod;
}
int main(){
	nn=po(2,mod-2);
	scanf("%lld%lld",&n,&m);
	if(n<m){
		swap(n,m);
	}
	pre();
	for(long long i=1;i<=m;i++){
		ans=(ans+C(n+m-2*i,m-i)*C(2*i,i)%mod)%mod;
	}
	ans=ans*po(C(n+m,n),mod-2)%mod*nn%mod;
	ans=(ans+n)%mod;
	cout<<ans;
	return 0;
}

upd:降紫了(2023-10-14)

posted @ 2023-10-01 11:04  Airposs  阅读(31)  评论(0)    收藏  举报