题目链接
A. Joey Takes Money
题目描述
input
output
题意:
有一个长度为n的数组,你可以进行一步操作,选择两个i,j,然后再选择两个数x,y,使得a[i]a[j] = xy,然后将a[i] = x,a[j] = y,问任意步操作后的数组的最大和是多少。
思路:
两个数的乘积相等的话要是让他们加和尽可能大的话就是两个数相差尽可能大,那就是答案就是所有数的乘积然后n-1个1,下面看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n;
scanf("%lld",&n);
int ans = 1;
for(int i=1;i<=n;i++){
int x;
scanf("%lld",&x);
ans *= x;
}
printf("%lld\n",(ans+n-1)*2022);
}
signed main(){
int _;
for(cin>>_;_;_--) solve();
return 0;
}
B. Kill Demodogs
题目描述
题意:
给你一个数n,有一个大小为nn的矩阵,第i行第j列的单元的权值是ij,现在你可以向右走或者向下走,现在问你能获得的权值的最大值是多少?
思路:
这个跟上一个题刚好是相反的,这个是让i和j尽量相等,走对角线,然后就是右下右下这么走,先右再下,对角线是一个完全平方和,可以用n*(n+1)*(2n+1)/6这个公式来实现,那么对角线上面那一个斜线的和就是每次+1,+2,+3,…,+n-1,画一下就出来了,然后因为要取模,所以说除法得用费马小定理处理一下,下面看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
int ksm(int x,int n){
int ans = 1;
while(n){
if(n & 1) ans = ans * x % mod;
n >>= 1;
x = x*x%mod;
}
return ans;
}
void solve(){
int n;
scanf("%lld",&n);
printf("%lld\n",(n*(n+1)%mod*(2*n+1)%mod*ksm(3,mod-2)%mod-n*(n+1)%mod*ksm(2,mod-2)%mod+mod)*2022%mod);
}
signed main(){
int _;
for(cin>>_;_;_--) solve();
return 0;
}
C. Even Subarrays
题意:
给你一个数组,问有多少个区间满足区间的异或和的因子个数是偶数
思路:
除了完全平方数,其他的数的因子个数都是偶数,所以说只要是求出来有多少个完全平方数就行了,做法就是整一个数组记录前缀异或和,然后对于当前的数,只要求出来有多少个左边界使得这个区间的异或和为完全平方数就行了,因为每一个数都是小于等于n的,所以说直接用数组来维护就行了,用map的话会超时,因为本来复杂度已经是nsqrt(n)了,会被卡常,而且注意要开四倍空间,因为运行中的话会数组越界,这个地方需要点细节,下面看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
const int N = 8e5+10;
int mp[N];
void solve(){
int n;
scanf("%lld",&n);
for(int i=0;i<=n*2;i++) mp[i] = 0;
int ans = 0,k = 0;
mp[0] = 1;
for(int i=1;i<=n;i++){
int x;
scanf("%lld",&x);
for(int j=0;j*j<=n*2;j++){
ans += mp[(j*j)^k^x];
}
k ^= x;
mp[k]++;
}
printf("%lld\n",n*(n+1)/2-ans);
}
signed main(){
int _;
for(cin>>_;_;_--) solve();
return 0;
}
D. Valiant’s New Map
题意:
给你一个nm的矩阵,让你找到一个最大的size,使得存在一个sizsiz的方阵,方阵中的所有数都大于size
思路:
二分size,对于每一个size,首先预处理一遍,将数组中的比size大的数都变成size,然后前缀和就行,下面看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7,N = 2e6+10;
int n,m;
int g[N],f[N],s[N];
inline int find(int i,int j){
return i*m + j;
}
bool check(int siz){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
s[find(i,j)] = min(siz,g[find(i,j)]);
f[find(i,j)] = f[find(i-1,j)] + f[find(i,j-1)] - f[find(i-1,j-1)] + s[find(i,j)];
}
}
for(int i=siz;i<=n;i++){
for(int j=siz;j<=m;j++){
int x1 = i-siz+1,y1 = j-siz+1;
int t = f[find(i,j)] - f[find(x1-1,j)] - f[find(i,y1-1)] + f[find(x1-1,y1-1)];
if(t >= siz*siz*siz) return true;
}
}
return false;
}
void solve(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&g[find(i,j)]);
}
}
int l = 1,r = min(n,m),ans = -1;
while(l <= r){
int mid = l + r >> 1;
if(check(mid)) ans = mid,l = mid + 1;
else r = mid - 1;
}
printf("%lld\n",ans);
}
signed main(){
int _;
for(cin>>_;_;_--) solve();
return 0;
}
E. Graph Cost
题意:
给你一个n和m,现在有一个点数为n的图,一开始是没有边的,有一种操作,你可以选一个数k,然后加k+1条边,这些边的两个端点的最大公约数必须是k,不能出现重边,自环,每进行一步操作就会产生k+1的代价,现在问你正好添加m条边的最小代价是多少,如果没有的话就输出-1。
思路:
首先要预处理出来每一个最大公约数有多少点对,这个有两种办法,莫比乌斯反演和dp,我这里用的是dp,首先k的倍数有n/k个,然后只需要k的一倍就行,所以说就可以从大到小dp,然后调和级数算出来时间复杂度是O(nlogn)
,然后从大到小贪心就行,下面看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
int dp[N];
void solve(){
int n,m;
scanf("%lld%lld",&n,&m);
for(int i=n;i>=2;i--){
dp[i] = (n/i)*(n/i-1)/2;
for(int j=2*i;j<=n;j+=i) dp[i] -= dp[j];
}
int ans = 0;
for(int i=n;i>=2;i--){
if(m < i-1) continue;
int k = m / (i-1),l = dp[i] / (i-1);
m -= min(k,l)*(i-1);
ans += min(k,l)*i;
if(m == 0) break;
}
if(m) printf("-1\n");
else printf("%lld\n",ans);
}
signed main(){
int _;
for(cin>>_;_;_--) solve();
return 0;
}