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;
}