Codeforces Round #511 (Div. 2) A~D题解

A. Little C Loves 3 I

题目大意:给定一个\(n\)构造\(a+b+c=n\)且三者都不是3的倍数.

思路

显然直接讨论\(n\)即可.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main()
{
	int n;scanf("%d",&n);
	int a = n / 3,b = n / 3,c = n - a - b;
	if(n % 3 == 0)
	{
		if(a % 3 == 0)	b += 2,--a,--c;
	}
	else if(n % 3 == 1)
	{
		if(a % 3 == 0)	++b,--a;
		else if(c % 3 == 0)	++c,--a;
	}
	else if(n % 3 == 2)
	{
		if(a % 3 == 0)	++b,--a;
		else if(c % 3 == 0)	--c,++b;
	}
	printf("%d %d %d",a,b,c);
    return 0;
}

B. Cover Points

题目大意:平面上有\(n\)个点,找一个等腰三角形,两条边平行于坐标轴,且所有点在被覆盖的范围内.输出三角形最短一遍的长度.

思路

题面不知道怎么写的.

由于条件太强所以直线是\(y=-x\).所以直接讨论每个点被恰好覆盖到的时候,需要的长度就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main()
{
	int n;scanf("%d",&n);
	int res = 0;
	while(n--)
	{
		int x,y;scanf("%d%d",&x,&y);
		res = max(res,x + y);
	}
	printf("%d",res);
    return 0;
}

C. Enlarge GCD

题目大意:有\(n\)个数,求最少删除多少个数能使整个数列的\(gcd\)上升.无解输出-1.

数据范围:

\(2 \leq n \leq 3 *10^5\)

\(1 \leq a_i \leq 1.5*10^7\)

思路

一开始把所有的数直接除掉最开始的数列的\(gcd\).之后拿到的数列\(gcd=1\).

现在的问题就是最少删除多少个数可以使整个数列的\(gcd>1\).

那么显然也是套路,枚举每个质数\(p\)在所有数中出现的次数\(c\),那么\(n-c\)就是答案.排除掉\(c=0\)的情况找最小值就可以了.

但是这个题比较无聊,他需要特殊实现,为了卡根号算法导致这个题很无语:

枚举\(p\)的时候不直接枚举所有的数,而是枚举数域,也就是说在之前先在数域上把每个数出现的次数找出来,之后再枚举\(p\)以及\(p\)的倍数看有多少个位置被覆盖了,这样枚举倍数的做法类似于区间筛约数,复杂度是调和级数.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 3e5+7,M = 1.5e7+7;
int a[N];
int primes[M / 10],cnt;
bool st[M];
int c[M];

void init(int N)
{
	for(int i = 2;i <= N;++i)
	{
		if(!st[i])	primes[cnt++] = i;
		for(int j = 0;j < cnt && primes[j] * i <= N;++j)
		{
			st[i * primes[j]] = 1;
			if(i % primes[j] == 0)	break;
		}
	}
}

int gcd(int x,int y)
{
	if(y == 0)	return x;
	return gcd(y,x % y);
}

int main()
{
	int n,g = 0;scanf("%d",&n);
	forn(i,1,n)	scanf("%d",&a[i]),g = gcd(g,a[i]);
	forn(i,1,n)	a[i] /= g;
    
    int maxv = 0;
    forn(i,1,n)	maxv = max(maxv,a[i]),++c[a[i]];

    init(maxv);
    
    int res = n;
    forn(j,0,cnt - 1)
    {
    	int t = 0;
    	for(int p = primes[j];p <= maxv;p += primes[j])	t += c[p];
		if(t == 0 || t == n)	continue;
    	res = min(res,n - t);
    }
    
    printf("%d",res == n ? -1 : res);
    return 0;
}

D. Little C Loves 3 II

题目大意:有一个\(n*m\)的棋盘,每次可以往棋盘上放两个棋子,要求棋子不能重合且两个放置的棋子的曼哈顿距离必须是\(3\).求最多能放置多少个棋子.

数据范围:

\(1 \leq n,m \leq 10^9\)

思路

直接大力讨论:

首先保证\(n\leq m\).

  • \(n=1\)时,可以覆盖的范围以\(6\)为循环,那么按循环节来做,都有的部分就是\(n/6*6\),剩下的只有当\(n\%6=4\)时多加两个,\(n\%6=5\)时多加四个.
  • \(n=2\)时,可以找出来\(2X4,2X5,2X6\)的格子都是可以摆满的,根据赛瓦韦斯特定理,形如\(4x+5y=c\)的表达式,当\(x,y\)都是非负整数时,最大的不能表示出来的数是\(x*y-x-y\).也就是说最大的不能表示出来的数是\(11\),只需要在\(11\)范围之内特判,其他的都是可以表示出来的,那么当\(m=1,m=2\)时结果为\(0\),当\(m=3\)时结果为\(4\),当\(m=7\)时结果为\(12\).其余情况答案都是\(m*2\).
  • \(n\geq3\)时,棋局是可以任意摆满的,当\(n*m\)是奇数时多了一个格子删去,偶数是答案就是\(n*m\).证明可以通过一些可以摆满的块与上面相同来说明,例如\(3X3\)的恰好值多一个,\(4X4\)\(3X4\)的可以摆满,结合第二个就可以做完了.

还有一种思路是,因为距离为三所以显然可以二染色棋盘进而是一个二分图,问题等价于在二分图上求匹配.剩下的就是套路了,通过打表可以发现数值较大的时候答案就是\(n*m(-1)\).小范围的直接做二分图最大匹配就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	ll n,m;cin >> n >> m;
	if(n > m)	swap(n,m);
	
	if(n == 1)	
	{
		if(m % 6 == 0)	cout << m << endl;
		else
		{
			ll res = m / 6 * 6;
			if(m % 6 == 4)	res += 2;
			else if(m % 6 == 5)	res += 4;
			cout << res << endl;
		}
	}
	else if(n == 2)
	{
		if(m == 1)	cout << 0;
		else if(m == 2)	cout << 0;
		else if(m == 3)	cout << 4;
		else if(m == 7)	cout << 12;
		else cout << m * 2;
	}
	else
	{
		ll res = n * m;
		if(res & 1)	--res;
		cout << res << endl;
	}
    return 0;
}
posted @ 2021-01-25 13:36  随处可见的阿宅  阅读(116)  评论(0编辑  收藏  举报