20211106NOIP模拟赛

前言

芜湖起飞!!国际金牌出的题就是比国内金牌出的题好做!!!
成功的狗到了160分(据说T2输出0有十分??)

T1

一道逆元题:
得分:\(\color{green}{100}\)
考场上青禾巨佬12分钟切掉(但被卡了10分),本蒟蒻两个半小时才做对\(QWQ\)
分析题意,我们可以把它转换为抛骰子的问题,为抛到一种情况的期望次数。
像抛到6的期望就是抛到6的概率的倒数,就是6次。
再来看这道题,序列有序的概率是\(\frac{1}{序列的排列个数}\),所以期望次数就是序列的排列次数。
我们又开始求序列的排列个数了,由于有重复元素所以不好求。而蒟蒻用打表的方法找到了规律!!!

\[序列排列个数=\frac{n!}{\prod_{i=1}^{n}{cnt[val[i]]!}} \]

就这样,蒟蒻水题成功!(但还是因为不会打逆元而卡住了两个小时)

逆元就\(powfast\)求一下。
代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
map<int,int> q;
int n,a[1000001];
long long mul[1000001],frac=1;
bool flag=1;
void init(){
	mul[0]=1;
	mul[1]=1;
	for(int i=2;i<=n;i++){
		mul[i]=(long long)mul[i-1]*i%mod;
		mul[i]%=mod;
	}
	return ;
}
inline int read(){
	static char ch;
	int res=0,sign=1;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-'){
			sign=-1;
		}
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10+ch-'0';
	}
	return res*sign;
}
int temp[1000001];
long long tot=0;
long long fast_pow(long long x,long long y){
	long long res=1;
	while(y){
		if(y&1){
			res=res*x%mod;
		}
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
signed main(){
	freopen("bogo.in","r",stdin);
	freopen("bogo.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		if(i>1){
			if(a[i]<a[i-1]){
				flag=0;
			}
		}
	}
	init();
	if(flag){
		putchar('0');
		return 0;
	}else{
		memset(temp,0,sizeof temp);
		sort(a+1,a+n+1);
		for(int i=1;i<=n;i++){
			q[a[i]]++;
		}
		int qq=0;
		tot=mul[n]%mod;
		for(int i=1;i<=n;i++){
			if(a[i]!=a[i-1]){
				temp[++qq]=q[a[i]];
			}
		}
		frac=1;
		for(int i=1;i<=qq;i++){
			if(temp[i]==1){
				continue;
			}else{
				frac=frac*(mul[temp[i]])%mod;
				frac%=mod;
			}
		}
		cout<<((long long)(tot)%mod*fast_pow(frac,mod-2)%mod)%mod<<endl;
	}
	return 0;
}

T2

得分:\(\color{red}{0}\)
蒟蒻考场上在做完T1后,先把T3的暴力打了50分(加O2卡到60分),然后倒回来做T2,看题觉得是DP但是懒得推,于是搜索打挂了。

分析:首先,我们用一个三维数组来维护,在前n的数中,我们拿走了j个权值为1的点,还有k个权值为2的点,所浪费的空间。递推后,再贪心算空间。
\(O(n^3)\)

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,w,a[10001],su,q[10001];
short f[405][405][405];
int c[3];
int main()
{
	scanf("%d %d",&n,&w);
	for(int i=1;i<=n;i++){
		scanf("%d",a+i);
		q[i]=q[i-1]+a[i];
		su+=a[i],++c[a[i]];//su 前缀和,c统计次数 
	}
	if(su<=w){
		cout<<0<<endl;
		return 0;
	}
	memset(f,127/3,sizeof f);
	f[0][0][0]=0;
	auto upd=[&](short&a,int b){
		if(a>b) a=b;
	};
	for(int i=0;i<n;i++){
		for(int j=0;j<=i;j++){
			for(int k=0;j+k<=i;k++){
				if(f[i][j][k]<=8000){
					int tw=q[i]-j-k*2+f[i][j][k];
					if(tw%w+a[i+1]<=w){//不用开新的包,浪费和原来一样 
						upd(f[i+1][j][k],f[i][j][k]);
					}else{
						upd(f[i+1][j][k],f[i][j][k]+w-tw%w);//开新包,加上浪费的空间 
					}
					if(a[i+1]==1){
						upd(f[i+1][j+1][k],f[i][j][k]);//拿走下一个 
					}
					if(a[i+1]==2){
						upd(f[i+1][j][k+1],f[i][j][k]);
					}
				}
			}
		}
	}
	pair<int,int> ans(2e9,2e9);
	for(int j=0;j<=n;j++){
		for(int k=0;j+k<=n;k++){
			if(f[n][j][k]>=0){
				int tw=q[n]-j-k*2+f[n][j][k];//放的总空间 
				int K=k;
				int J=j;//贪心把拿出来的放进去, 
				while(K){
					if(tw%w+2<=w) --K,tw+=2;//先放大的 
					else if(J) --J,tw+=1;//再放小的补位 
					else --K,tw+=3;//没有小的就假装有小的打包一起放 
				}
				while(J) --J,tw+=1;//放入 
				ans=min(ans,pair<int,int>((tw+w-1)/w,j+k));//比较用包的个数 
			}
		}
	}
	cout<<ans.second<<endl;
	
	return 0;
}

T3

得分:\(\color{orange}{60}\)
算法一:暴力搜索:
期望得分:50~60分

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
long long weight[100001];
char sit[100001];
int book[100001];
struct node{
	long long sum;
	char op;
}e[100001];
bool dfs(int num,long long left,long long right){
	if(num>n){
		for(int i=1;i<=n;i++){
			printf("%lld %c\n",e[i].sum,e[i].op);
		}
		return 1;
	}
	int flag=0;
	if(sit[num]=='L'){
		for(int i=1;i<=n;i++){
			if(!book[i]&&left+weight[i]>right){
				book[i]=1;
				e[num].sum=weight[i];
				e[num].op='L';
				flag=dfs(num+1,left+weight[i],right);
				if(flag){
					return 1;
				}
				book[i]=0;
			}
			if(!book[i]&&left>weight[i]+right){
				book[i]=1;
				e[num].sum=weight[i];
				e[num].op='R';
				flag=dfs(num+1,left,right+weight[i]);
				if(flag){
					return 1;
				}
				book[i]=0;
			}
		}
	}else{
		for(int i=1;i<=n;i++){
			if(!book[i]&&left<right+weight[i]){
				book[i]=1;
				e[num].sum=weight[i];
				e[num].op='R';
				flag=dfs(num+1,left,right+weight[i]);
				if(flag){
					return 1;
				}
				book[i]=0;
			}
			if(!book[i]&&left+weight[i]<right){
				book[i]=1;
				e[num].sum=weight[i];
				e[num].op='L';
				flag=dfs(num+1,left+weight[i],right);
				if(flag){
					return 1;
				}
				book[i]=0;
			}
		}
	}
	return 0;
}
inline int read(){
	static char ch;
	int res=0,sign=1;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-'){
			sign=-1;
		}
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10+ch-'0';
	}
	return res*sign;
}

int main(){
	freopen("weight.in","r",stdin);
	freopen("weight.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		weight[i]=(long long)read();
	}
	sort(weight+1,weight+n+1);
	cin>>sit+1;
	dfs(1,0,0);
	
	return 0;
}

算法二:
很妙的方法:
20211106NOIP模拟赛
就是,如果连续相同的条件,我们就一边放\(a_{m-i-1}\),一边放\(a_{m-i}\),如果不一样,就在一边加\(a_{m+i}\),一边放\(a_{m+i+1}\)
代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,a[100001],b[100001];
char c[100001];
string s;
int main(){
	
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	cin>>s;
	sort(a,a+n);
	a[n]=2e9;
	int r=-1;
	int op=0;
	for(int i=0;i<n;i++){
		if(i&&s[i]==s[i-1]){
			++r;//确定m的位置 
		}
	}
	int l=r+1;
	for(int i=0;i<n;i++){
		if(i&&s[i]==s[i-1]){
			c[i]=op^=1;//上一个操作异或 
			b[i]=a[r--];//a_m-1
		}else{
			c[i]=(s[i]=='R');
			b[i]=a[l++];//a_m+i+1
			if(!i) op=(s[i]=='R');
		}
	}
	for(int i=0;i<n;i++)
	{
		cout<<b[i]<<' '<<("LR"[c[i]])<<"\n";
	}
	return 0;
}

好的,蒟蒻非常开心\(QWQ\)

上一篇:css开头格式


下一篇:css第一部分学习