Codeforces Round #680 (Div. 2)题解+补题

A.Array Rearrangment:

题目链接:https://codeforces.ml/contest/1445/problem/A

题目大意:

给定n,x以及长度为n的两个序列a,b,要求对a,b重新排序后,使得对于任意的i有a[i]+b[i]≤x成立

题目分析:

1.首先对a排序形成不下降序列,对b排序形成不上升序列。检查此时是否对于任意的i有a[i]+b[i]≤x成立。成立输出YES,否则输出NO。
2.证明此时是所有a[i]+b[i]的最大值最小的序列:

  • 设排序后的a中有a[i]≤a[j](i≤j),设排序后的b中有b[i]≥b[j](i≤j),形成的和为a[i]+b[i]a[j]+b[j],最大值为max(a[i]+b[i],a[j]+b[j])
  • 交换b[i]b[j],形成的和为a[i]+b[j]a[j]+b[i],最大值为max(a[i]+b[j],a[j]+b[i]),显然此时的最大值大于原序列。证明完毕

正解程序:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>

using namespace std;
typedef long long ll;
const ll maxn=100;
ll a[maxn],b[maxn],x;
ll n,T;

int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld",&n,&x);
		for(ll i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		for(ll i=1;i<=n;i++)
			scanf("%lld",&b[i]);
		ll flag=0;
		for(ll i=1;i<=n;i++)
		{
			if(a[i]+b[n-i+1]>x)
			{
				flag=1;
				break;
			}
		}
		if(flag==0)
			printf("YES\n");
		else
			printf("NO\n");
	}
	
	return 0;
}

B.Elimination:

题目链接:https://codeforces.ml/contest/1445/problem/B

题目大意:

一个比赛有两场分比赛,每场比赛按照分数从高到低排名,分数相同时按照序号排序,每个榜单只有前100人。两场比赛后给出一个总排名,总排名按照两场比赛得分和进行从高到低排名,现在总榜丢失,只有前两场比赛的榜单,现在知道第一场的第一百名得a分,且第一场前100名在第二场至少获得b分。第二场的第100名获得c分,且第二场前100名在第一场都至少获得d分,现在问总榜的第100名最少是多少分。

题目分析:

根据题意,那么我们可以得知我们至少有100人获得a+b分,至少100人获得c+d分,所以排下来第100名的最小值就是max(a+b,c+d)

正解代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>

using namespace std;
typedef long long ll;
ll T;
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		ll a,b,c,d;
		scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
		printf("%lld\n",max(a+b,c+d));
	}
	
	return 0;
}

C. Division:

题目链接:https://codeforces.ml/contest/1445/problem/C

题目大意:

有t组测试数据,每组给出了p和q ,要求找出最大的x使得p能被x整除,且x不能被q整除。

题目分析:

1.首先我们发现如果p%q!=0,则p就是答案,因为此时p%p==0 && p%q!=0。满足条件,而大于p的数则不再能整除p。
2.因为此时p是q的倍数,所以q的质因数p一定有,所以我们分别将p,q分解质因数,得到p=a1k1 * a2k2 ······ ankn,q=a1b1 * a2b2 ······ ankn。(k1 ~ kn大于0,b1 ~ bn大于等于0)
3.因为满足条件的x一定是p的因数,且不能是q的倍数。所以最大的值就是q中所有质因数的幂变得和p中对应数的幂相同,选择其中一个数使得它的幂是p中对应数幂减一。例如:
p=a1k1 * a2k2 * a3k3(k1 ~ k3大于0)
q=a1b1 * a2b2 * a3b3(b1 ~ b3大于等于0)
则x=max(a1k1-1 * a2k2 * a3k3,a1k1 * a2k2-1 * a3k3,a1k1 * a2k2 * a3k3-1)

正解代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>

using namespace std;
typedef long long ll;
const ll maxn=40010;
ll T,prime[maxn],count1=0;
bool vis[maxn];
ll act[maxn],pos=0,now[maxn];
void oula()
{
	memset(vis,false,sizeof(vis));
    for(ll i=2;i<=maxn-10;i++)
	{
        if(!vis[i])
			prime[++count1]=i;
        for(ll j=1;j<=count1 && i*prime[j]<=maxn-10;j++)
		{
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
				break;
        }
    }
}
ll quickmi(ll a,ll p)
{
	ll ans=1;
	while(p)
	{
		if(p&1)
			ans=ans*a;
		a=a*a;
		p>>=1;
	}
	return ans;
}
int main()
{
	oula();
	scanf("%lld",&T);
	while(T--)
	{
		pos=0;
		ll a,b;
		scanf("%lld%lld",&a,&b);
		if(a%b!=0)
			printf("%lld\n",a);
		else
		{
			for(ll i=1;i<=count1 && prime[i]<=b;i++)
			{
				if(b%prime[i]==0)
				{
					act[++pos]=prime[i];
					now[pos]=1;
					while(b%prime[i]==0)
					{
						b/=prime[i];
						now[pos]*=prime[i];
					}
				}
			}
			if(b!=1)
			{
				act[++pos]=b;
				now[pos]=b;
			}
			ll Min=1e18,used=a;
			for(ll i=1;i<=pos;i++)
			{
				ll aim=0;
				a/=now[i];
				while(a%act[i]==0)
				{
					a/=act[i];
					aim++;
				}
				Min=min(quickmi(act[i],aim+1),Min);
			}
			printf("%lld\n",used/Min);
		}
	}
	
	return 0;
}

D. Divide and Sum:

题目链接:https://codeforces.ml/contest/1445/problem/D

题目大意:

现在有一个序列有 2n 个元素,让你分成两个元素为 n 的子序列 p 和 q,将两个序列一个从大到小排序,一个从小到大排序,排序后设 f ( p , q ) = ∑ i = 1 n ∣ p i − q i ∣ f(p,q)=\sum_{i=1}^{n}|p_{i}-q_{i}| f(p,q)=i=1npiqi,问所有可能的子序列对应的f(p,q)之和为多少,答案对998244353取模。

题目分析:

1.对原序列升序排序,我们所选的p,q序列可以有如下方式得到:

  • 将原序列分为前后n个数组成的两个序列,称为序列1和序列2
  • p即在序列1中选择x个,在序列2中选择n-x个
  • q即在序列1中选择n-x个,在序列2中选择x个。

2.此时无论x是多少,所选的x个数是多少, f ( p , q ) = ∑ i = 1 n ∣ p i − q i ∣ f(p,q)=\sum_{i=1}^{n}|p_{i}-q_{i}| f(p,q)=i=1npiqi= ∑ i = 1 n ( q i − p i ) \sum_{i=1}^{n}(q_{i}-p_{i}) i=1n(qipi)=sum(定值)。所以最后的答案就是sum * C(2n,n)。
3.因为有取余运算,求组合数涉及除法,所以要使用乘法逆元,因为模数是质数,所以利用费马定理求出逆元即可。

正解程序:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>

using namespace std;
typedef long long ll;
const ll maxn=300010;
const ll mod=998244353;
ll a[maxn],fac[maxn],n;
ll quickmi(ll a,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)
			ans=ans*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return ans;
}
void init()
{
	fac[0]=1;
	for(ll i=1;i<=maxn-10;i++)
        fac[i]=fac[i-1]*i%mod;
}
int main()
{
	init();
    scanf("%lld",&n);
    for(ll i=1;i<=2*n;i++)
        scanf("%lld",&a[i]);
    sort(a+1,a+2*n+1);
    ll sum=0,fina=0;
    for(ll i=1;i<=n;i++)
        sum=(sum+a[i+n]-a[i])%mod;
    ll temp=quickmi(fac[n],mod-2);
    fina=sum*fac[2*n]%mod*temp%mod*temp%mod;
    printf("%lld\n",fina);
    
	return 0;
}

E. Long Permutation:

题目链接:

题目大意:

题目分析:

正解代码:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值