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)
思路
直接大力讨论:
首先保证(nleq 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).
- 在(ngeq3)时,棋局是可以任意摆满的,当(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;
}