The 2021 Shanghai Collegiate Programming Contest重现

A.小 A 的点面论

题目大意:
输出与两个向量同时垂直的向量

注意:所得结果需为非零向量!
思路:
1、数据很小,直接暴力求解
两向量垂直:两向量的数量积为0
a ⋅ b = a x b x + a y b y + a z b z a\cdot b = a_xb_x+ a_yb_y+a_zb_z ab=axbx+ayby+azbz

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int main(){
	int x1,y1,z1,x2,y2,z2;
	scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
	for(int i=-200;i<=200;i++){
		for(int j=-200;j<=200;j++){
			for(int k=-200;k<=200;k++){
				int a=x1*i+y1*j+z1*k;
				int b=x2*i+y2*j+z2*k;
			//	cout<<i<<" "<<j<<" "<<k<<endl;
				if(i==0&&j==0&&k==0) continue;//是否为零向量
				if(a==0&&b==0){
					printf("%d %d %d\n",i,j,k);
					return 0;
				}
			}
		}
	} 
    return 0;
}

2、两向量的叉乘(向量积)即为答案
a × b = ( a y b z − a z b y ) + ( a z b x − a x b z ) + ( a x b y − a y b x ) a\times b = (a_yb_z-a_zb_y)+(a_zb_x-a_xb_z)+(a_xb_y-a_yb_x) a×b=(aybzazby)+(azbxaxbz)+(axbyaybx)

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int main(){
	int x1,y1,z1,x2,y2,z2;
	scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
	int x,y,z;
	x=y1*z2-z1*y2;
	y=z1*x2-x1*z2;
	z=x1*y2-y1*x2;
	printf("%d %d %d\n",x,y,z);
	return 0;
}

C. 小 A 的期末考试

题目大意:
给出n个人的成绩和小A的学号,如果小A的成绩小于60分则将其改成60分,如果除了小A之外有人的成绩大于等于平均分(按所有人初始成绩计算),小 A 会把他的成绩改低 2 分,但不会低于 0 分。
给出每个人被修改后的成绩。

思路:模拟

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int a[maxn];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	double sum=0;
	for(int i=1;i<=n;i++){
		int id,grade;
		scanf("%d%d",&id,&grade);
		sum+=grade;
		a[id]=grade;
	}
	sum/=n;
	for(int i=1;i<=n;i++){
		if(i==m&&a[i]<60) a[i]=60;
		else if(i!=m&&a[i]>=sum){
			a[i]-=2;
			if(a[i]<0) a[i]=0;
		}
		printf("%d",a[i]);
		if(i==n) printf("\n");
		else printf(" ");
	}
	return 0;
}

E. Zztrans 的庄园

题目大意:
一共五种鱼,每种鱼对应不同的豆子数。
每次给出鱼塘里鱼的数量和钓鱼的次数
然后给出能钓到每种鱼的概率,求期望收益多少豆子。

思路:
因为钓到n条鱼的总概率为1,所以每一次钓鱼都必定会钓到鱼,不会落空。而且每一次钓鱼的结果都是独立的,所以只要算出钓一次鱼能收获的豆子数,最后乘以钓鱼次数即可。
期望定义式:
E ( X ) = ∑ i = 1 n p i × x i E(X)=\sum_{i=1}^np_i\times x_i E(X)=i=1npi×xi

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int a[maxn];
double b[maxn];
int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	getchar();
	double ans=0;
	map<char,int>mp;
	mp['D']=16;mp['C']=24;mp['B']=54;mp['A']=80;mp['S']=10000;
	for(int i=0;i<n;i++){
		char ch;
		scanf("%c %lf",&ch,&b[i]);
		a[i]=mp[ch];
		getchar();//注意每次吸收换行!!
	}
	for(int i=0;i<n;i++){
		ans+=a[i]*b[i];
	}
	printf("%.6lf\n",(ans-23)*k);
    return 0;
}

G. 鸡哥的雕像

题目大意:
给出n个数, a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an
第i个位置输出:除 a i a_i ai以外的数字的乘积对998244353 取模后的结果。

思路:
1、大数模拟:会T
2、边取模边乘,再除以相应数:答案改变
3、前缀乘+后缀乘
去除 a i a_i ai之后,其实将所有数分成了两个部分:
a i a_i ai前面所有数字的乘积、 a i a_i ai后面所有数字的乘积
预处理出前缀乘和后缀乘,即可用O(n)的复杂度求解

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
ll a[maxn];
ll b[maxn];
int x[maxn];
int main(){
	int n;
	scanf("%d",&n);
	a[0]=1;
	for(int i=1;i<=n;i++){//前缀乘
		scanf("%d",&x[i]);
		if(i==1) a[i]=x[i];
		else a[i]=(a[i-1]*x[i])%mod;
	}
	b[n+1]=1;
	for(int i=n;i>=1;i--){//后缀乘
		if(i==n) b[i]=x[i];
		else b[i]=(b[i+1]*x[i])%mod;
	}
	for(int i=1;i<=n;i++){
		ll ans=(a[i-1]%mod*(b[i+1]%mod))%mod;
		printf("%lld",ans);
		if(i==n) printf("\n");
		else printf(" ");
	}
	return 0;
}

4、逆元
利用乘法取模性质:
( a ∗ b ) % m o d = ( ( a % m o d ) ∗ ( b % m o d ) ) % m o d (a*b)\%mod=((a\%mod)*(b\%mod))\%mod (ab)%mod=((a%mod)(b%mod))%mod
又因为本题mod=998244353 <1e9
所以可以进一步优化解法:
当出现多于两个mod时,取模后的结果一定全为0
若只有一个mod,那只有去除这个mod时,取模后的结果不为0,否则全为0

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int x[maxn];
ll ksm(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans%mod;
}
int main(){
	int n;
	scanf("%d",&n);
	ll sum=1;
	int cnt=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&x[i]);
		if(x[i]==mod) cnt++;
		else sum=(sum*x[i])%mod;
	}
	if(cnt>=2){
		for(int i=1;i<=n;i++){
			printf("0");
			if(i==n) printf("\n");
			else printf(" ");
		}
	}
	else if(cnt==1){
		for(int i=1;i<=n;i++){
			if(x[i]==mod) printf("%lld",sum);
			else printf("0");
			if(i==n) printf("\n");
			else printf(" ");
		}
	}
	else{
		for(int i=1;i<=n;i++){
			ll ans=(sum*ksm(x[i],mod-2))%mod;
			printf("%lld",ans);
			if(i==n) printf("\n");
			else printf(" ");
		}
	}
	return 0;
}

J. Alice and Bob-1

题目大意:
n张卡片,每张卡片都有一个数值。Alice和Bob轮流交替取卡片,直到取完。
每个人手中卡片的价值是所取得的卡片数值和的绝对值。
A = ∣ ∑ j = 1 k a j ∣ A=|\sum_{j=1}^ka_j| A=j=1kaj
B = ∣ ∑ i = 1 n a i − ∑ j = 1 k a j ∣ B=|\sum_{i=1}^na_i-\sum_{j=1}^ka_j| B=i=1naij=1kaj
Alice想让她取得的卡片的价值相比于Bob所取得的尽可能大,而Bob不想被Alice拉开差距,即:
Alice希望 A − B A-B AB尽可能大,而Bob希望 A − B A-B AB尽可能小。
两人均采取最优策略,求最终 A − B A-B AB等于多少。

思路:
最优策略:
Alice每次都拿当前数值最大的卡片
Bob每次都拿当前第二大的卡片
但要注意负数的影响,所以需要从大到小取一次,从小到大取一次,然后Alice取两次的最大值,Bob取两次的最小值。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
ll a[maxn];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%lld",&a[i]);
	}
	sort(a,a+n);
	ll A1=0,A2=0,B1=0,B2=0;
	for(int i=n-1;i>=0;i-=2){
		A1+=a[i];
		B1+=a[i-1];
	}
	for(int i=0;i<n;i+=2){
		A2+=a[i];
		B2+=a[i+1];
	}
	//cout<<A1<<" "<<A2<<" "<<B1<<" "<<B2<<endl;
	A1=max(abs(A1),abs(A2)),B1=min(abs(B1),abs(B2));
	printf("%lld\n",A1-B1);
	return 0;
}

D. Zztrans 的班级合照

题目大意:
已知每个人身高从低到高排序后的排名(身高相同的人,排名也相同)
现需按如下要求排队:
排成人数相同的两排,每排从左到右身高都不递减,且第二排的同学的身高不低于第一排对应位置同学的身高。
求有多少种排队的方案,输出方案总数对 998244353取模后的结果。

思路:
1、求方案数,一般会用dp
d p [ i ] [ j ] dp[i][j] dp[i][j]表示已经排好前i个人,且第一行比第二行多j个人的方案数
2、对身高相同的人的处理:缩点
身高一样,排队的方案数取决于相同身高的人数有多少,试想其他人都已经排好,只剩下x个身高相同的人还没有排队,则这x个人排队的方案数为: x ! x! x!
3、状态转移方程:
当前状态: d p [ i ] [ j ] dp[i][j] dp[i][j]
设当前身高相同的人的数量为num
上一状态:
假设当前状态第一行有 x + j x+j x+j个人,第二行则有x个人,若当前状态是在上一状态的基础之上往第一行再排k个人,往第二行再排 n u m − k num-k numk个人得到的,那上一状态第一行有 x + j − k x+j-k x+jk个人,第二行有 x − ( n u m − k ) x-(num-k) x(numk)个人;则上一状态第一行人数比第二行多 x + j − k − ( x − ( n u m − k ) ) = j + n u m − 2 k x+j-k-(x-(num-k))=j+num-2k x+jk(x(numk))=j+num2k个人,
即上一状态为: d p [ i − 1 ] [ j + n u m − 2 k ] dp[i-1][j+num-2k] dp[i1][j+num2k]
在上一状态再排num个身高相同的人的总方案数即为 d p [ i − 1 ] [ j + n u m − 2 k ] ∗ n u m ! dp[i-1][j+num-2k]*num! dp[i1][j+num2k]num!
但由于这num个人的分配方式不止一种(k的取值不止一种),所以需要将k从0到num进行遍历,将所有可能的方案都算上。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int a[maxn];
int vis[maxn];
ll dp[maxn][maxn];
ll f[maxn];
void init(){
	f[1]=1;
	for(int i=2;i<=5005;i++){
		f[i]=(f[i-1]*i)%mod; 
	}
}
int main(){
	int n;
	cin>>n;
	init();
	for(int i=1;i<=n;i++){
		cin>>a[i];
		vis[a[i]]++;
	}
	sort(a+1,a+n+1);
	int cnt=unique(a+1,a+n+1)-(a+1);//去重(将相同身高的人,缩成一个点) 
	dp[0][0]=1;//已排好0个人,且第一行比第二行多0个人的方案数为1
	for(int i=1;i<=cnt;i++){
		int num=vis[a[i]];
		for(int j=0;j<=n;j++){
			for(int k=0;k<=num;k++){
				int l=j+num-2*k;
				if(l<0) break;
				dp[i][j]=(dp[i][j]+(dp[i-1][l]*f[num])%mod)%mod;	
			}
		} 
	} 
	cout<<dp[cnt][0]<<endl;
	return 0;
}

B. 小 A 的卡牌游戏

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
ll dp[maxn][maxn];
struct card{
	int a,b,c;
	bool operator <(const card&y) const{
		if(b-a == y.b-y.a) return c>y.c;
		return b-a>y.b-y.a;
	}
}p[maxn]; 
int main(){
	int n,A,B,C;
	scanf("%d%d%d%d",&n,&A,&B,&C);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
	}
	sort(p+1,p+n+1);
	for(int i = 0; i <= n; i++)
		for(int j = i+1; j <= C; j++)
			dp[i][j] = -1e9;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=min(C,i);j++){
			if(j) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+p[i].c);
			if(i-j<=B) dp[i][j]=max(dp[i][j],dp[i-1][j]+p[i].b);
			else dp[i][j]=max(dp[i][j],dp[i-1][j]+p[i].a);
		}
	}
	cout<<dp[n][C]<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值