牛客练习赛38 BCD题解

最近放学回家,咸鱼了一段日子,现在滚出来写题了。

B. 出题人的女装

这活生生一个高中数学题,但是我竟然WA了12次,看来数学水平退步了。

首先第一件衣服是女装,那么有可能是在第一个箱子选的,也有可能是第二个箱子选的,第一个箱子选女装的概率是x/n,第二个箱子选女装的概率是y/m,那么箱子是第一个的概率就是x/n / (x/n+y/m),是第二个的概率就是y/m /(x/n+y/m),然后就是水题啦。

#include<bits/stdc++.h>
#define db double
#define ll long long
using namespace std;
ll gcd(ll x,ll y)
{
	if(!y)return x;
	return gcd(y,x%y);
}
int main()
{
	ll n,m,x,y,t;
	cin>>n>>m>>x>>y>>t;
	ll zi=y*(y-1)*(n-1)*n+(x-1)*x*m*(m-1);
	ll mu=(n-1)*(m-1)*(x*m+y*n);
	ll yin=gcd(mu,zi);
	zi/=yin,mu/=yin;
	if(t==1)
	printf("%lld/%lld\n",zi,mu);
	if(t==0)
	{
		db ans=db(zi)/mu;
		ans=ans*1000+0.5;
		int tmp=(int)ans;
		if(ans>=1000)puts("1.000");
		else
		{
			if(ans<10)printf("0.00");
			else if(ans<100)printf("0.0");
			else printf("0.");
			printf("%d\n",tmp);
		}
	}
}

C. 出题人的矩阵

比赛迟到了半个多小时,也在赛后刚好30分钟1A了这个题.....

我们分析一下,3*3的矩阵填写1到9这些数字,是不是一共有9!也就是362880种填法?那么我可以用dfs把所有合法的矩阵找出来,然后把这些合法矩阵当做多个起点跑最短路,每个矩阵对应每个点,开个数组记录9!个矩阵的最短路值,然后就可以直接输出答案咯。

#include<bits/stdc++.h>
#define db double
#define ll long long
using namespace std;
const int maxn=4e5; 
int a[4][4],b[4][4],vis[10],f[maxn],d[maxn],tot;
int check()
{
	int ans=0;
	for(int i=1;i<4;i++)
	ans+=a[1][i];
	for(int i=1;i<4;i++)
	{
		if(ans!=a[i][1]+a[i][2]+a[i][3])return 0;
		if(ans!=a[1][i]+a[2][i]+a[3][i])return 0;
	}
	if(ans!=a[1][1]+a[2][2]+a[3][3])return 0;
	if(ans!=a[3][1]+a[2][2]+a[1][3])return 0;
	return 1;
}
int id(int t[4][4])//把每个矩阵都换成对应的点 
{
	int res=0;
	for(int i=1;i<4;i++)
	for(int j=1;j<4;j++)
	res=res*10+t[i][j];
	return lower_bound(f+1,f+1+tot,res)-f;//矩阵换成数在有序数组的下标是对应的点 
}
struct node
{
	int c[4][4];
	node(int t[4][4])
	{
		for(int i=1;i<4;i++)
		for(int j=1;j<4;j++)
		c[i][j]=t[i][j];
	}
};
queue<node>q;
void bfs()//求多源最短路 
{
	while(!q.empty())
	{
		node e=q.front();q.pop();
		int cur=id(e.c);
		int dis=d[cur];
		for(int i=1;i<4;i++)
		for(int j=1;j<4;j++)
		b[i][j]=e.c[i][j];
		for(int i=1;i<=3;i++)
		{
			swap(b[1][i],b[2][i]);
			int t=id(b);
			if(d[t]>d[cur]+1)
			d[t]=d[cur]+1,q.push(node(b));
			swap(b[1][i],b[2][i]);
			
			swap(b[3][i],b[2][i]);
			t=id(b);
			if(d[t]>d[cur]+1)
			d[t]=d[cur]+1,q.push(node(b));
			swap(b[3][i],b[2][i]);

			swap(b[i][1],b[i][2]);
			t=id(b);
			if(d[t]>d[cur]+1)
			d[t]=d[cur]+1,q.push(node(b));
			swap(b[i][1],b[i][2]);
			
			swap(b[i][3],b[i][2]);
			t=id(b);
			if(d[t]>d[cur]+1)
			d[t]=d[cur]+1,q.push(node(b));
			swap(b[i][3],b[i][2]);		
		}
	}
}
void dfs(int x,int y)
{
	if(x==4)
	{
		if(check())
		{
			int cur=id(a);
			d[cur]=0;
			q.push(node(a));
		}
		return;
	}
	for(int i=1;i<10;i++)
	if(!vis[i])
	{
		a[x][y]=i;
		vis[i]=1;
		if(y!=3)dfs(x,y+1);
		else dfs(x+1,1);
		vis[i]=0;
	}
}
void dfs2(int x,int v)//找到1到9的所有排列,每个矩阵对应每个排列 
{
	if(x==10)
	{
		f[++tot]=v;
		return;
	}
	for(int i=1;i<=9;i++)
	if(!vis[i])
	{
		vis[i]=1;
		dfs2(x+1,v*10+i);
		vis[i]=0;
	}
}
int main()
{
	for(int i=0;i<maxn;i++)
	d[i]=1e9;
	dfs2(1,0);
	sort(f+1,f+1+tot);
	dfs(1,1);
	bfs();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		for(int i=1;i<4;i++)
		for(int j=1;j<4;j++)
		scanf("%d",&b[i][j]);
		int cur=id(b);
		printf("%d\n",d[cur]); 
	}
}

D. 出题人的手环

这个题其实很水,假设有数据1 3 2 4,首先我用树状数组求出原始的逆序数为p=1,假设我把4删除,假设t1为数组中比4大的数,t2为数组中比4小的数,那么删除后数组逆序数是p-t1,然后我在第一个位置插入一个4,新的逆序数就是p-t1+t2,没错,就这样借树状数组递推求出所有情况的逆序数就可以了,复杂度n*logn

#include<bits/stdc++.h>
#define ll long long
#define low(x) x&-x
using namespace std;
const int maxn=2e5+10,mod=1e9+7;
int a[maxn],b[maxn],c[maxn],d[maxn],n;
int qu(int x)
{
	int res=0;
	while(x<=n)
	res+=c[x],x+=low(x);
	return res;
}
void up(int x)
{
	while(x)
	c[x]++,x-=low(x);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+1+n,a[i])-b;	
		d[a[i]]++;
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
	{
		ans+=qu(a[i]+1);
		up(a[i]);
	}
	ans%=mod;
	ll p=ans;
	for(int i=n;i>1;i--)
	{
		int t1=qu(a[i]+1);
		int t2=n-d[a[i]]-t1;
		p=(p+t2-t1+mod)%mod;
		ans=ans*p%mod;
	}
	cout<<ans;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值