Atcoder Beginner Contest 169题解

A

Description

给定 a a a b b b,输出 a × b a×b a×b

Solution

直接模拟即可。

Code

#include 
#define int long long
using namespace std;

int a,b;

signed main()
{
	cin>>a>>b;
	cout<<a*b<<endl;
	
	return 0;
}

B

Description

给定 n n n个数,请求出它们的乘积。特别的,如果该值大于 1 0 18 10^{18} 1018则输出 − 1 -1 1

Solution

显然,由题意可以写出这个式子:

if (tot*a[i]>1000000000000000000)  return cout<<-1<<endl,0;

注意,这里的 t o t ∗ a i tot*a_i totai会溢出!既然乘法不行,就用除法呗~

if (tot>1000000000000000000/a[i])  return cout<<-1<<endl,0;

注意,这两个式子可以互相转换的,依据是不等式的基本性质。

还有一件重要的事情——在判断溢出前,看一下给定的那些数中是否有 0 0 0,如果有就直接输出 0 0 0并结束程序。

Code

#include 
#define int long long
#define inf 1000000000000000000
using namespace std;

int n,tot=1,a[200005];

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i];
	for (int i=1;i<=n;i++)
	{
		if (a[i]==0)  return cout<<0<<endl,0;
	}
	for (int i=1;i<=n;i++)
	{
		if (tot>inf/a[i])  return cout<<-1<<endl,0;
		tot*=a[i];
	}
	cout<<tot<<endl;
	
	return 0;
}

C

不会做……

D

Description

尽可能多地把 n n n拆分为多个不同的,且是某个质数的正整数次方数 的数的乘积,并输出最多的个数。

Solution

做法显然。先质因数分解,即 n = ∏ i = 1 k a i b i n=∏_{i=1}^k a_i^{b_i} n=i=1kaibi

可以得到,对于每对 ( a i , b i ) (a_i,b_i) (ai,bi),我们需要将 b i b_i bi拆分为 1 + 2 + 3 + … … + x + y 1+2+3+……+x+y 1+2+3++x+y,并且要拆分得尽可能地多;所以,假设最多可以拆分为 n o w now now个,那么答案就是 ∑ n o w \sum now now

注意质因数分解的时间复杂度需要限制在 O ( n ) O(\sqrt n) O(n )以内,如果不会可以看我的另外一篇博客——质因数分解详解。

Code

#include 
#define int long long
using namespace std;

int n,ans=0;

signed main()
{
	cin>>n;
	if (n==1)  return cout<<0<<endl,0;
	
	for (int i=2;i*i<=n;i++)
	{
		if (n%i==0)
		{
			int pos=0;
			while (n%i==0)  n=n/i,pos++;
			
			int now=sqrt(pos*2);
			if (now*(now+1)>pos*2)  now--;
			
			ans+=now;
		}
	}
	if (n>1)  ans++;
	cout<<ans<<endl;
	
	return 0;
}

E

Description

给定 n n n l i l_i li r i r_i ri,且数组 a a a满足 l i ≤ a i ≤ r i ( 1 ≤ i ≤ n ) l_i≤a_i≤r_i(1≤i≤n) liairi(1in)。现在,请求出数组 a a a有多少种不同的中位数

solution

显然,我们要分奇偶类讨论。

n n n为奇数。
容易发现,此时中位数仅仅与 a a a数组中最中间的那个数 x x x有关。于是,我们将 l l l数组与 r r r数组按升序排序,那么有 l m i d ≤ x ≤ r m i d ( m i d = ( n + 1 ) / 2 ) l_{mid}≤x≤r_{mid}(mid=(n+1)/2) lmidxrmid(mid=(n+1)/2)。所以,此时中位数有 r m i d − l m i d + 1 r_{mid}-l_{mid}+1 rmidlmid+1种。

n n n为偶数。
容易发现,此时中位数与 a a a数组中最中间的那两个数 x , y x,y x,y有关;只要 x + y x+y x+y的值不同,那么中位数就不同(中位数为 x + y 2 \frac {x+y} 2 2x+y)。于是,我们将 l l l数组与 r r r数组按升序排序,那么有 l l m i d + r l m i d ≤ x + y ≤ l r m i d + r r m i d l_{lmid}+r_{lmid}≤x+y≤l_{rmid}+r_{rmid} llmid+rlmidx+ylrmid+rrmid,其中 ( l m i d = n / 2 , r m i d = l m i d + 1 ) (lmid=n/2,rmid=lmid+1) (lmid=n/2,rmid=lmid+1)。所以,此时中位数有 l r m i d + r r m i d − l l m i d − r l m i d + 1 l_{rmid}+r_{rmid}-l_{lmid}-r_{lmid}+1 lrmid+rrmidllmidrlmid+1种。

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

显然不可能做到这么优秀。

排序时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
总时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

Code

#include 
#define int long long
using namespace std;

int n,ans;
int a[200005],b[200005];

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i]>>b[i];
	
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	
	if (n%2==0)  ans=b[n/2]+b[(n/2)+1]-a[n/2]-a[(n/2)+1]+1;
	else ans=b[(n+1)/2]-a[(n+1)/2]+1;
	
	cout<<ans<<endl;
	
	return 0;  
}

F

Description

给定集合 a a a(暂且当 a a a中数可以重复),请问存在多少个不同的 a a a的子集使得子集中存在子集的和为 s s s?

由于答案可能过大,请将其对 998244353 998244353 998244353取模。

Solution

显然,如果集合 a a a的某个大小为 k k k的子集 S S S满足要求,那么它对答案的贡献就是 2 n − k 2^{n-k} 2nk

然后设计状态,其中 d p i dp_i dpi表示所有数和为 i i i的子集对答案的贡献之和

接着,考虑状态转移。如果某个和为 s u m 1 sum_1 sum1的子集大小为 k k k,添加了一个数 a i a_i ai后成为了一个和为 s u m 1 + a i sum_1+a_i sum1+ai且大小为 k + 1 k+1 k+1的子集,贡献也除以了2(贡献从 2 n − k 2^{n-k} 2nk变成了 2 n − k − 1 2^{n-k-1} 2nk1)。

所以,状态转移就是: d p j + = d p [ j − a i ] / 2 dp_j+=dp[j-a_i]/2 dpj+=dp[jai]/2

但是,状态转移涉及到除法与取模,所以除以2要用逆元

时间复杂度: O ( n 2 ) O(n^2) O(n2)

Code

#include 
#define int long long
using namespace std;
const int mod=998244353;

int n,s;
int a[3005],dp[3005];

int quick_power(int x,int y)
{
	int res=1;
	for (;y;y=y>>1,x=(x*x)%mod)
	{
		if (y&1)  res=(res*x)%mod;
	}
	return res;
}

signed main()
{
	cin>>n>>s;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	dp[0]=quick_power(2,n);
	for (int i=1;i<=n;i++)
	{
		for (int j=s;j>=a[i];j--)  dp[j]=(dp[j]+(dp[j-a[i]]*499122177)%mod)%mod;
	}
	cout<<dp[s]<<endl;
	
	return 0;
}

总结

排名没进前3500,太惨了,赛后诸葛亮……

①A题切掉;
②B题WA了10次才AC,罚时50分钟;甚至有几次提交了同样的WA代码;心态崩溃;
④D题切掉;
⑤E题看都没看,直接搞B题去了……比赛后看到E题才发现如此简单;
⑥F题快速幂板子打错,且忘记逆元,不知道在搞啥QWQ

最终死得非常惨,大家不要学我这个菜鸡啊~

我艹%&#&@*

你可能感兴趣的:(自闭场,比赛题解,数学,线性代数,算法)