A. Joey Takes Money
样例输入:
3
3
2 3 2
2
1 3
3
1000000 1000000 1
样例输出:
28308
8088
2022000000004044
题意:给定一个长度为n的数组,我们可以对这个数组中的元素做任意多次操作,每次操作选择一个点对(i,j),然后将a[i]和a[j]替换为x和y,满足a[i]*a[j]=x*y,对整个数组操作结束后,求数组中所有元素和的最大值,输出最大值乘以2022.
分析:由基本不等式得a+b>=2sqrt(ab),当乘积一定时,a和b差值越大,那么a+b的值就越大,这是可以通过对勾函数看出来的,所以我们直接对a和b用ab和1两个数替换即可。这样我们对n个数操作后的结果就是n-1个1和一个n个数的乘积。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
long long ans=1;
for(int i=1;i<=n;i++)
{
long long t;
scanf("%lld",&t);
ans*=t;
}
printf("%lld\n",(ans+n-1)*2022);
}
return 0;
}
B. Kill Demodogs
样例输入:
4
2
3
50
1000000000
样例输出:
14154
44484
171010650
999589541
题意:给定一个n*n的矩阵,矩阵(i,j)的权值就是i*j,现在让我们找出一条从(1,1)的到(n,n)的路径使得路径上的权值和最大,每次只能向右走或者向下走。现在输出最大权值和对1e9+7取余后的结果。
分析:容易发现选择右-下-……-右-下-右-下一定是一条最优的路径,因为在a+b恒定的时候,a和b相差越小那么就会有a*b越大,所以尽量靠着对角线走是最优的。那么我们可以发现答案就是,那么就等于2*n*(2*n+1)*(n+1)/6-n*(n+1)/2。需要注意的是因为答案要对1e9+7取余,所以我们会用到费马定理+快速幂,不能直接除。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int mod=1e9+7;
long long qpow(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
int T;
cin>>T;
while(T--)
{
long long n;
scanf("%lld",&n);
long long ans=n*(2*n+1)%mod*(n+1)%mod*qpow(3,mod-2)%mod;
ans=(ans+mod-n*(n+1)/2%mod)%mod;
printf("%lld\n",(ans*2022)%mod);
}
return 0;
}
C. Even Subarrays
样例输入:
4
3
3 1 2
5
4 2 1 5 3
4
4 4 4 4
7
5 7 3 7 1 7 3
样例输出:
4
11
0
20
题意:给定一个长度为n的数组,然后问有多少个区间[l,r]满足区间内所有数的异或值含有偶数个因子。
分析:先来看一下含有奇数个因子的数具有什么特征,对于一个数x=p1^a1*p2^a2*……*pn^an,那么他的因子个数就是(a1+1)*(a2+1)*……*(an+1),那么只要有一个ai是奇数,那么因子个数就是偶数,那么也就是说只有幂次全部都为偶数时这个数含有的因子个数才是奇数,所以也就是说只有平方数含有的因子个数才是奇数,数据范围是2e5,那么这里面含有的因子个数也不超过1e3,所以我们可以直接找区间异或和为平方数的区间个数,用总的区间个数减去不满足题意的区间个数就可以得到符合题意的区间个数。对于一个平方数x,要想找寻区间异或和等于x的区间个数我们可以用前缀异或优化,这样复杂度就是O(n),所以总的复杂度就是O(n^1.5)。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=8e5+5;
int tt,p[N],s[N];
int main()
{
int T;
cin>>T;
p[++tt]=0;
for(int i=1;i*i<N;i++)
p[++tt]=i*i;
while(T--)
{
int n,t,ss=0;//ss记录当前异或和
scanf("%d",&n);
for(int i=0;i<=2*n;i++)
s[i]=0;
s[0]=1;
long long ans=1ll*n*(n+1)/2;
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
ss^=t;
for(int j=1;p[j]<=2*n;j++)
ans-=s[p[j]^ss];
s[ss]++;
}
printf("%lld\n",ans);
}
return 0;
}
D. Valiant's New Map
样例输入:
4
2 2
2 3
4 5
1 3
1 2 3
2 3
4 4 3
2 1 4
5 6
1 9 4 6 5 8
10 9 5 8 11 6
24 42 32 8 11 1
23 1 9 69 13 3
13 22 60 12 14 17
样例输出:
2
1
1
3
题意:给定一个n*m的矩阵,每一个位置都有一个数,让我们找到一个最大的l,满足矩阵中存在一个l*l的方阵满足方阵中的数的值都是大于等于l的。
分析:这道题直接二分l就行,对于每个l我们先处理原矩阵,对于原矩阵中大于等于l的数我们就将其变为l,否则变为0,我们只需要扫一遍矩阵就可以知道是否存在l*l的矩阵满足里面所有的数都不小于l了,这个用二维前缀和优化一下就行,因为给的条件是n*m<1e6,所以我们直接将二维映射到一维即可。代码复杂度就是n*m*log(min(n,m))
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10;
long long s[N],a[N],n,m;
int find(int x,int y)
{
if(x<1) return 0;
return (x-1)*m+y;
}
bool check(int l)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(a[find(i,j)]>=l) s[find(i,j)]=l;
else s[find(i,j)]=0;
s[find(i,j)]+=s[find(i-1,j)]+s[find(i,j-1)]-s[find(i-1,j-1)];
}
for(int i=l;i<=n;i++)
for(int j=l;j<=m;j++)
if(s[find(i,j)]-s[find(i-l,j)]-s[find(i,j-l)]+s[find(i-l,j-l)]==1ll*l*l*l) return true;
return false;
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n*m;i++) a[i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[find(i,j)]);
int l=1,r=min(n,m);
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
E. Graph Cost
样例输入:
4
4 1
6 10
9 4
10 11
样例输出:
2
-1
7
21
题意:一开始给定一个n个节点的空图,节点编号为1~n,对于节点u和v,我们只能加入一条权值为gcd(u,v)的边,现在我们可以对这个图进行操作,每次操作选定一个k,向图中加入k条权值为k+1的边,代价是k+1,注意加的边不能是自环,并且两个点之间只能加入一条边,不能有重边。现在让我们往图中加入m条边,问最少需要的代价是多少?如果不能加入m条边就输出-1.
分析:首先我们需要处理一下f[i],f[i]记录满足gcd(u,v)=i的点对(u,v)的对数,这个直接用动态规划求解即可。首先由于1~n中两个不同数的最大公约数最大是n/2,所以对于所有的i>n/2都有f[i]=0,然后我们可以发现对于一个k,所有含有因子k的数一共有n/k个,除法默认都是下取整,那么从n/k个数里面随便取出两个数,那么最大公约数都是k的倍数,那么也就是C(n/k,2),其中含有最大公约数是2*k的,3*k的……n/k*k的,这正好对应着f[2*k],f[3*k],……,f[n/k*k],所以我们可以直接利用动态规划求解这个问题,复杂度正好满足调和级数,也就是nlogn,所以我们对于任意一个k,都可以求出来最大公约数为k的点对数。
然后为了使得代价最小,我们直接选取权值尽可能大的边加入,所以我们就可以直接从n往2贪心加,如果最后凑不成m就输出-1即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10;
long long f[N];//f[i]记录满足gcd(u,v)=i的点对(u,v)的对数
int main()
{
int T;
cin>>T;
while(T--)
{
long long n,m;
scanf("%lld%lld",&n,&m);
for(int i=n/2+1;i<=n;i++)
f[i]=0;
for(int i=n/2;i>=1;i--)
{
f[i]=(n/i)*(n/i-1)/2;
for(int j=2;j*i<=n;j++)
f[i]-=f[i*j];
}
long long ans=0;
while(m&&n>1)
{
if(m>=n-1)
{
long long t=min(f[n]/(n-1),m/(n-1));
ans+=t*n;
m-=t*(n-1);
}
n--;
}
if(m==0) printf("%lld\n",ans);
else puts("-1");
}
return 0;
}