A. Odd Divisor
题目链接:点击此处
每个大于等于2的整数都可以划分为质数的积,然后质数只有2是偶数。所以我们对于一个数除完2,看看是否为1,为1就NO,大于1就是YES。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
ll sum[MAXN];
int main() {
int t;
cin >> t;
while (t--) {
cin >> n;
while (n % 2 == 0) {
n /= 2;
}
if (n == 1) { cout << "NO" << endl; }
else cout << "YES" << endl;
}
}
B. New Year’s Number
题目链接:点击此处
观察一个数是否被 2020 2020 2020和 2021 2021 2021组成,很明显的是 2021 = 2020 + 1 2021=2020+1 2021=2020+1,所以如果这个数 a i a_i ai符合要求,那么 a i = 2020 ∗ x + 2021 ∗ y = 2020 ∗ ( x + y ) + y a_i=2020*x+2021*y=2020*(x+y)+y ai=2020∗x+2021∗y=2020∗(x+y)+y,所以取余2020,会得到 y y y,然后 a i a_i ai减去 2021 ∗ y 2021*y 2021∗y,观察是否大于等于 0 0 0,并且能被 2020 2020 2020取余,能的话YES,不能NO。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
ll sum[MAXN];
int main() {
int t;
cin >> t;
while (t--) {
cin >> n;
ll a = n % 2020;
ll sum = n - a * 2021;
if (sum < 0 || sum % 2020 != 0) {
cout << "NO" << endl;
}
else cout << "YES" << endl;
}
}
C. Ball in Berlan
题目链接:点击此处
给我n个数,那么我们可以得到 ( n − 1 ) ∗ n 2 \frac{(n-1)*n}{2} 2(n−1)∗n组,但是我们要减去不符合的。即出现重复的。这里我们要知道一个性质。如果 a i a_i ai和 a j a_j aj重复,那么 b i b_i bi和 b j b_j bj不会重复,因为如果重复,那么就是同一组了,题意不允许出现相同两组。
所以我们遍历数组到 a i a_i ai时,假设前面有 k 1 k_1 k1个与 a i a_i ai重复, k 2 k_2 k2个与 b i b_i bi重复,那么 a n s − = k 1 + k 2 ans-=k_1+k_2 ans−=k1+k2。因为这些组不能和 a i , b i {a_i,b_i} ai,bi组一起。对于 a i , b i {a_i,b_i} ai,bi后续的组,如果出现与 a i , b i {a_i,b_i} ai,bi重复,那么通过后续的组来删去。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll a1[MAXN] = { 0 }, b1[MAXN] = { 0 };
ll arr[MAXN];
int main() {
int t;
cin >> t;
while (t--) {
ll k;
cin >> n >> m >> k;
ll ans = (k - 1) * k / 2;
for (int i = 1;i <= k;i++) {
cin >> a[i];
ans -= a1[a[i]];
a1[a[i]]++;
}
for (int i = 1;i <= k;i++) {
cin >> b[i];
ans -= b1[b[i]];
b1[b[i]]++;
}
for (int i = 1;i <= k;i++) {
a1[a[i]] = 0;
b1[b[i]] = 0;
}
cout << ans << endl;
}
}
D. Cleaning the Phone
题目链接:点击此处
c 1 [ i ] c1[i] c1[i]为第 i i i大的代价为1的容量。
c 2 [ i ] c2[i] c2[i]为第 i i i大的代价为2的容量。
d 1 [ i ] d1[i] d1[i]表示花费 i i i代价得到的最大容量后,选了几个代价为1的。
d 2 [ i ] d2[i] d2[i]表示花费 i i i代价得到的最大容量后,选了几个代价为2的。
我用的是DP, d p [ i ] dp[i] dp[i]表示花费了 i i i代价,最多能消除多少体积。那么题目转化很简单, d p [ i ] = m a x ( d p [ i − 1 ] + c 1 [ d 1 [ i − 1 ] ] , d p [ i − 2 ] + c 2 [ d 2 [ i − 2 ] ] , d p [ i − 2 ] + c 1 [ d 1 [ i − 2 ] ] + c 1 [ d 1 [ i − 2 ] + 1 ] ) dp[i]=max(dp[i-1]+c1[d1[i-1]],dp[i-2]+c2[d2[i-2]],dp[i-2]+c1[d1[i-2]]+c1[d1[i-2]+1]) dp[i]=max(dp[i−1]+c1[d1[i−1]],dp[i−2]+c2[d2[i−2]],dp[i−2]+c1[d1[i−2]]+c1[d1[i−2]+1]),即现在花费 i i i的最大值只能由花费 i − 2 i-2 i−2的加上一个代价2的或者两个代价1的,或者花费 i − 1 i-1 i−1的加上一个代价1的。之后得到相应最大值后,把 d 1 [ i ] d1[i] d1[i]和 d 2 [ i ] d2[i] d2[i]随之更新一下就行。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 5e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll c1[MAXN], c2[MAXN];
ll dp[MAXN];
ll d1[MAXN], d2[MAXN];
bool cmp(ll& a, ll& b) {
return a > b;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n >> m;
int cnt1 = 0, cnt2 = 0;
int sum = 0;
for (int i = 1;i <= n;i++)cin >> a[i];
for (int i = 1;i <= n;i++) {
cin >> b[i];
sum += b[i];
if (b[i] == 1)c1[++cnt1] = a[i];
else c2[++cnt2] = a[i];
}
sort(c1 + 1, c1 + 1 + cnt1,cmp);
sort(c2 + 1, c2 + 1 + cnt2, cmp);
d1[0] = d2[0] = 1;
dp[0] = 0;
dp[1] = c1[1];
d1[1] = 2;d2[1] = 1;
if (dp[1] >= m) { cout << 1 << endl;
//初始化
d1[0] = d2[0] = dp[0] = dp[1] = d1[1] = d2[1] = 0;
for (int i = 1;i <= cnt1;i++)c1[i] = 0;
for (int i = 1;i <= cnt2;i++)c2[i] = 0;
continue;
}
bool f = false;
for (int i = 2;i <= sum;i++) {
int k = 0;
int tmp = 0;
if (c1[d1[i - 2]] + c1[d1[i - 2] + 1] > c2[d2[i - 2]]) {
tmp = c1[d1[i - 2]] + c1[d1[i - 2] + 1];
dp[i] = dp[i - 2] + tmp;
d1[i] = d1[i - 2] + 2;
d2[i] = d2[i - 1];
}
else {
tmp = c2[d2[i - 2]];
dp[i] = dp[i - 2] + tmp;
d2[i] = d2[i - 2] + 1;
d1[i] = d1[i - 2];
}
if (tmp + dp[i - 2] < dp[i - 1] + c1[d1[i - 1]]) {
tmp = c1[d1[i - 1]];
dp[i] = dp[i - 2] + tmp;
d1[i] = d1[i - 1] + 1;
d2[i] = d2[i - 1];
}
if (dp[i] >= m) { cout << i << endl;f = true;break; }
}
//初始化
for (int i = 1;i <= cnt1;i++)c1[i] = 0;
for (int i = 1;i <= cnt2;i++)c2[i] = 0;
for (int i = 0;i <= sum;i++)dp[i] = d1[i] = d2[i] = 0;
if (!f)cout << -1 << endl;
}
}
E. Advertising Agency
题目链接:点击此处
贪心,所以先对数组排序,肯定要拿前k个。情况很多是因为有很多 a r r [ i ] arr[i] arr[i]可能是等于 a r r [ k ] arr[k] arr[k]的,那么我们定一个 p r e pre pre和 l a s t last last标记,表示第一个值等于 a r r [ k ] arr[k] arr[k]的下标,和最后一个等于 a r r [ k ] arr[k] arr[k]的下标,然后 l a s t − p r e + 1 last-pre+1 last−pre+1得到 a r r [ k ] arr[k] arr[k]有几个。之后就是组合数+逆元求解。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
bool cmp(ll& a, ll& b) {
return a > b;
}
void extend_gcd(ll a, ll b, ll& x, ll& y) {
if (b == 0) {
x = 1, y = 0;
return;
}
extend_gcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - (a / b) * y;
}
//逆元
ll mod_inverse(ll a, ll b) {
ll x, y;
extend_gcd(a, b, x, y);
//防止负数
return (x % b + b) % b;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1;i <= n;i++)cin >> arr[i];
sort(arr + 1, arr + 1 + n,cmp);
int pre = MAXN;
int last = 0;
for (int i = 1;i <= n;i++) {
if (arr[i] == arr[m]) {
pre = min(pre, i);
last = max(last, i);
}
}
ll a = last - pre + 1;
ll b = m - pre + 1;
ll ans = 1;
for (int i = a;i >= b + 1;i--)ans = ans * i % mod;
for (int i = 1;i <= a - b;i++) {
ans = ans * mod_inverse(i, mod)%mod;
}
cout << ans << endl;
}
}
F. Unusual Matrix
题目链接:点击此处
很容易知道一行或者一列异或后,会使得所有值翻转。
一个数翻转2次等于没动。
我们枚举矩阵,当枚举到 i , j i,j i,j时,前面枚举到的点都已经匹配完了。如果在该点需要翻转,那么我们不能列翻转,因为翻转列,会使得该列上面几行的点匹配不成功,所以我们只能翻转行,但是翻转行又有问题了,因为该行前面的点已经匹配完了。翻转行会使得前面的点不匹配。有人说,那么在下一行我们翻转列就行,但是我们这样会使得该列最上面那些匹配好的点不匹配,形成矛盾。
所以我们了解到,在一定程度后,我们就不能再翻转了,如果出现没匹配的,那么答案肯定是匹配不了的。
那么我们要知道什么条件才不能翻转。
那就是出现后效性,即我们翻转这个会使得我们前面匹配完的还要翻转就不行。
那么哪些点没有后效性呢?就是第一列的所有点的行翻转无后效性。第一行的所有点的列翻转无后效性。
所以答案很显然了,我们对第一行和第一列进行行列翻转让他们满足要求,之后遍历其他点,如果出现不符合的就是错的。
代码中 m p [ i ] [ j ] = = 1 mp[i][j]==1 mp[i][j]==1表示要翻转的,为0表示不用翻转, h o r [ j ] hor[j] hor[j]和 v e r [ i ] ver[i] ver[i]表示第j行,第 i i i列是否翻转过。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int mp[MAXN][MAXN];
int ver[MAXN];
int hor[MAXN];
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
char ch;
cin >> ch;
mp[i][j] = ch - '0';
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
char ch;
cin >> ch;
mp[i][j] = (mp[i][j] + (ch - '0')) % 2;
}
}
if (n == 1) {
cout << "YES" << endl;
continue;
}
bool f = true;
for (int i = 1;i <= n;i++) {
if (mp[1][i] == 1) {
hor[i] = 1;
}
}
for (int i = 2;i <= n;i++) {
if (hor[1])mp[i][1] = mp[i][1] ^ 1;
if (mp[i][1] == 1)ver[i] = 1;
else ver[i] = 0;
for (int j = 2;j <= n;j++) {
for (int k = 1;k <= ver[i] + hor[j];k++) {
mp[i][j] = mp[i][j] ^ 1;
}
if (mp[i][j] == 1) { f = false;break; }
}
if (!f)break;
}
for (int i = 1;i <= n;i++) {
ver[i] = hor[i] = 0;
}
if (f)cout << "YES" << endl;
else cout << "NO" << endl;
}
}
G. Strange Beauty
题目链接:点击此处
这题我们要知道如果 a a a能被 b b b整除, b b b能被 c c c整除,那么 a a a能被 c c c整除。
那么我们很容易想到递推。
d p [ b ] + = d p [ a ] dp[b]+=dp[a] dp[b]+=dp[a], d p [ c ] + = d p [ b ] dp[c]+=dp[b] dp[c]+=dp[b]
之后我们想枚举。发现枚举肯定会超时。
所以需要优化。 那么怎么才能知道一个数的整数倍有哪些呢?
我们知道一个数是由质数够成的,所以我们只要枚举质数就行。
所以 d p [ i ] = d p [ i ] + m a x ( d p [ i ∗ p r i m e [ 1 ] ] , d p [ i ∗ p r i m e [ 2 ] ] , . . . d p [ i ∗ p r i m e [ c n t ] ] ) dp[i]=dp[i]+max(dp[i*prime[1]],dp[i*prime[2]],...dp[i*prime[cnt]]) dp[i]=dp[i]+max(dp[i∗prime[1]],dp[i∗prime[2]],...dp[i∗prime[cnt]])
通过这个我们知道我们要从后往前枚举。其中 i i i就表示一个数 i i i,不是表示 a r r [ i ] arr[i] arr[i],如果表示 a r r [ i ] arr[i] arr[i]就会错误,比如样例 2 2 8,我们得到8的 d p dp dp值,但是不能通过 p r i m e prime prime得到2的dp值。只能通过4来作为间接值来得到,即 d p [ 4 ] = d p [ 4 ] + m a x ( d p [ 4 ∗ 2 ] , d p [ 4 ∗ 3 ] . . . ) , d p [ 2 ] = d p [ 2 ] + m a x ( d p [ 2 ∗ 2 ] , d p [ 2 ∗ 3 ] . . . ) dp[4]=dp[4]+max(dp[4*2],dp[4*3]...),dp[2]=dp[2]+max(dp[2*2],dp[2*3]...) dp[4]=dp[4]+max(dp[4∗2],dp[4∗3]...),dp[2]=dp[2]+max(dp[2∗2],dp[2∗3]...)。
但是这样枚举肯定复杂度很大,所以要优化,那么就是 i ∗ p r i m e [ j ] i*prime[j] i∗prime[j]大于2e5时,我们就 b r e a k break break,这样,我们会减少很多复杂度。
这边我们初始化 d p [ i ] dp[i] dp[i]表示在 a r r arr arr中的出现次数。
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int dp[MAXN];
int arr[MAXN];
bool visit[MAXN] = { 0 };
int prime[MAXN];
int cnt = 0;
//欧拉筛
void init() {
for (int i = 2;i < MAXN;i++) {
if (!visit[i])
prime[++cnt] = i;
for (int j = 1;j <= cnt, prime[j] * i < MAXN;j++) {
visit[prime[j] * i] = true;
if (i % prime[j] == 0)break;
}
}
}
int main() {
int t;
cin >> t;
init();
while (t--) {
int n;
cin >> n;
int ans = 1;
for (int i = 1;i <= n;i++) {
cin >> arr[i];
dp[arr[i]]++;
}
for (int i = 2e5;i >= 1;i--) {
int maxn = 0;
for (int j = 1;j <= cnt;j++) {
if (i * prime[j] > (int)2e5)break;
if (dp[i * prime[j]] != 0) {
maxn = max(maxn, dp[i * prime[j]]);
}
}
dp[i] += maxn;
ans = max(dp[i], ans);
}
for (int i = 1;i <= 2e5;i++) {
dp[i] = 0;
}
cout << n - ans << endl;
}
}