The 2021 Shanghai Collegiate Programming Contest(A B C D E G J)

题目链接:Dashboard - The 2021 Shanghai Collegiate Programming Contest - Codeforces

前言:剩余题目待补充

    

A - 小A的点面论

算法:几何知识

题解:答案为两个向量的叉积,(三维空间中,两个向量的叉积就是这两个向量确定平面的法向量) 可直接利用公式,也可以暴力枚举计算。

代码一:时间复杂度O(1)

#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 2e3 + 100;

struct node
{
	int x, y, z;
}s[N];

int main()
{
	IOS;
	cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z;
    
	cout<<s[1].y*s[2].z-s[1].z*s[2].y<<" ";
	cout<<s[1].z*s[2].x-s[1].x*s[2].z<<" ";
    cout<<s[1].x*s[2].y-s[1].y*s[2].x;
	
    return 0;
}

代码二:时间复杂度O(n^{3})

#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 2e3 + 100;

struct node
{
	int x, y, z;
}s[N];

int main()
{
	IOS;
	cin>>s[1].x>>s[1].y>>s[1].z>>s[2].x>>s[2].y>>s[2].z;

	for(int i = -200; i <= 200; i++)
	{
		for(int j = -200; j <= 200; j++)
		{
			for(int k = -200; k <= 200; k++)
			{
				if(i*s[1].x + j*s[1].y + k*s[1].z == 0 && i*s[2].x + j*s[2].y + k*s[2].z == 0)
				{
					cout<<i<<" "<<j<<" "<<k;
					return 0;
				}
			}
		}
	}
    return 0;
}

B - 小A的卡牌游戏

算法:dp

题解:如果题目简化为两组数a和b,则可按照b-a 降序排序,然后贪心地先选择b再选择a。基于此贪心思想,对本题使用dp,dp[i][j]表示在前i次选择中,有j次选择第三组卡牌c,剩余i-j次按照前面贪心思想选择剩余两组卡牌。每次状态转移时,比较前后两种状态(本次是否选择该组卡牌),取max即可。注意dp数组需初始化,最大值为-1e9,可更小。

代码:时间复杂度O(n^{2})

typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 5e3 + 100;

int n, A, B, C;

struct node
{
	int a, b, c;
	bool operator < (const node &y) const
	{
		if(b-a == y.b-y.a) return c > y.c;
		return b-a > y.b-y.a;
	}
}v[15010];

ll dp[N][N];

int main()
{
	IOS;
	cin>>n>>A>>B>>C;
	for(int i = 1; i <= n; i++) cin>>v[i].a>>v[i].b>>v[i].c;
	sort(v+1, v+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] + v[i].c);
			if(i-j <= B) dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].b);
			else dp[i][j] = max(dp[i][j], dp[i-1][j] + v[i].a);
		}
	}
	cout<<dp[n][C];
    return 0;
}

C - 小A的期末考试

算法:模拟

题解:简单模拟,注意输出的格式。

代码:时间复杂度O(n)

#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const double eps = 1e-6; 
const int N = 2e5 + 100;

int n, m, sum;
double mid;

struct node
{
	int a, b;
	bool operator < (const node &y) const
	{
		return a < y.a;
	}
}s[N];

int main()
{
	IOS;
	cin>>n>>m;
	for(int i = 1; i <= n; i++) 
	{
		cin>>s[i].a>>s[i].b;
		sum += s[i].b;
	}
	mid = 1.0*sum/n;
	for(int i = 1; i <= n; i++)
	{
		if(s[i].a == m) 
		{
			if(s[i].b < 60) s[i].b = 60;
		}
		else
		{
			if(1.0*s[i].b >= mid + eps) s[i].b -= 2;
			if(s[i].b < 0) s[i].b = 0;
		}
	}
	sort(s+1, s+n+1);
	for(int i = 1; i <= n-1; i++) cout<<s[i].b<<" ";
	cout<<s[n].b;
    return 0;
}

D - Zztrans的班级合照

算法:dp

题解:dp[i][j]表示两排共选择了i个人,第一排比第二排多j个人。使用数组cnt存储相同身高的人数,将身高升序排序,去重。第一层循环表示选择身高为a[i]的学生,第二层循环表示目前第一排比第二排多j个学生,第三层循环表示在身高为a[i]的学生中选择k个放在第二排,剩余cnt[a[i]]-k个放在第一排。(这k个学生放第一排或者放第二排都可,只是状态转移方程有一些不同)

        假设之前第一排有x+j个学生,第二排有x个学生,则放置k个学生之后,第一排学生总数变为x+j+cnt[a[i]]-k,第二排变为x+k,此时第一排和第二排的学生数量差值为now = j+cnt[a[i]]-2*k,当差值大于0的时候才是有效转移。所以现在的状态dp[i][now]可以从上次的的状态dp[i-1][j]转移,由于选择的是身高为a[i]的一类学生,学生们身高一样但代表着的还是不同的人,所以需要乘以该身高学生个数的阶乘。(阶乘在开始预处理并存储在数组里面)

代码:时间复杂度O(n^{2})

typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int mod = 998244353;
const int N = 5e3 + 100;

int n;
ll a[N], cnt[N], dp[N][N], fac[N];

void init_fac()
{
	fac[1] = 1;
	for(int i = 2; i <= N; i++) (fac[i] = fac[i-1]*i)%=mod;
}

int main()
{
	IOS;
	init_fac();
	cin>>n;
	for(int i = 1; i <= n; i++)
	{
		cin>>a[i];
		cnt[a[i]]++;
	}
	sort(a+1, a+n+1);
	int nn = unique(a+1, a+n+1)-(a+1);
	dp[0][0] = 1;
	for(int i = 1; i <= nn; i++)
	{
		for(int j = 0; j <= n; j++)
		{
			for(int k = 0; k <= cnt[a[i]]; k++)
			{
				int now = j+cnt[a[i]]-2*k;
				if(now < 0) break;
				(dp[i][now] += dp[i-1][j]*fac[cnt[a[i]]])%=mod;
			}
		}
	}
	cout<<dp[nn][0];
    return 0;
}

E - Zztrans的庄园

算法:概率与期望

题解:基本期望知识。期望定义式:

                                                        ​​​​​​​       E(X) = \sum_{i=1}^{n}p_{i}*x_{i}

代码:时间复杂度:O(n)

#define IOS ios::sync_with_stdio(0);cin.tie(0);

int n, k;
double sum;

int main()
{
	// IOS;
	scanf("%d %d", &n, &k);
	for(int i = 1; i <= n; i++)
	{
		char s; double x; cin>>s>>x;
		if(s == 'A') x *= 57;
		if(s == 'B') x *= 31;
		if(s == 'C') x *= 1;
		if(s == 'D') x *= -7;
		if(s == 'S') x *= 9977;
		sum += x;
	}
	printf("%.4f", sum*k);
    return 0;
}

G - 鸡哥的雕像

算法:逆元

题解:每个位置的答案就是所有的数的乘积除以该位置上的数字并对998244353取模,使用逆元计算sum/a[i],即sum*ksm(a[i], mod-2)(ksm为快速幂函数)。但由于998244353 < 1e9。所以分三种情况:给出的数中有两个及以上的998244353,这样的话所有的位置最终结果都为0;给出的数中有一位是998244353,则只有这一位上的结果为其它为上数的乘积,其他位全为0;没有出现998244353,则按照正常公式计算。

代码:时间复杂度O(n)

typedef long long ll;
#define endl "\n"
#define IOS ios::sync_with_stdio(0);cin.tie(0);

const int mod = 998244353;
const int N = 1e5 + 100;

ll ksm(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b&1) (res *= a)%=mod;
        (a *= a)%=mod;
        b>>=1;
    }
    return res;
}

ll n, cnt;
ll a[N];
int vis[N];

int main()
{
	IOS;
	ll sum = 1;
    cin>>n;
	for(int i = 1; i <= n; i++)
    {
		cin>>a[i];
		if(a[i] == mod) vis[i] = 1, cnt++;
		else (sum *= a[i])%=mod;
	}
    if(cnt >= 2)
    {
        for(int i = 1; i <= n-1; i++) cout<<0<<" ";
        cout<<0;
    }
    else if(cnt == 1)
    {
        for(int i = 1; i <= n-1; i++)
        {
            if(vis[i]) cout<<sum<<" ";
            else cout<<0<<" ";
        }
        if(vis[n]) cout<<sum;
        else cout<<0;
    }
	else
    {
        for(int i = 1; i <= n-1 ;i++) cout<<sum*ksm(a[i], mod-2)%mod<<" ";
        cout<<sum*ksm(a[n], mod-2)%mod;
    }
    return 0;
}

J - Alice and Bob 1

算法:思维

题解:若数据全为正值:在轮流取卡片的过程中,Alice拿当前最大的,Bob取第二大的即可保证Alice取得的为最大值。但由于题目最后的结果为绝对值且所给数据中有负值,所以在数据按递减排序后,会有Alice从前往后取,先取正值再取负值综合为正值,和从后往前取,先取负值再取正值总和为负值,这两种结果的绝对值都有可能是最大值。

        例如第一组数据: 5 3 2 1 -1 -2 这种情况下Alice要从前往后取。第二组数据:1 2 -3 -5 -7 -8这种情况下Alice要从后往前取。

代码:时间复杂度O(n)

typedef long long ll;
#define IOS ios::sync_with_stdio(0);cin.tie(0);
const int N = 5e3 + 100;

int n;
ll sum1, sum2, sum;
int a[N];

bool cmp(int x, int y)
{
	return x > y;
}

int main()
{
	IOS;
	cin>>n;
	for(int i = 1; i <= n; i++) cin>>a[i], sum += a[i];
	sort(a+1, a+n+1, cmp);
	for(int i = 1; i <= n; i += 2) sum1 += a[i];
	for(int i = n; i >= 1; i -= 2) sum2 += a[i];
	if(abs(sum1) > abs(sum2)) cout<<(ll)abs(sum1) - (ll)abs(sum-sum1);
	else cout<<(ll)abs(sum2) - (ll)abs(sum-sum2);
    return 0;
}

                                                         

                                                                    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值