你最愿意做的哪件事,才是你的天赋所在

0%

3.28训练总结

题目链接

莫干山网络赛

D - Find Integer

思路

根据费马大定理(虽然没有证明,但是这个数据是肯定成立的),n>2的时候无无解,然后就找n=2的时候,发现是勾股数。
然后这里提供一种勾股数的求法
1.a=2n+1的奇数且a>1,若$a^2+b^2=c^2 \&\&a<b<c 则b=2nn+2n,c=2nn+2n+1$
2.a=2
n的偶数且a>2,若$a^2+b^2=c^2\&\&a<b<c 则b=nn-1,c=nn+1$

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int t;
scanf("%d",&t);
while(t--){
ll n,a;
scanf("%lld %lld",&n,&a);
if(n>2)printf("-1 -1\n");
else{
if(n==0)printf("-1 -1\n");
else if(n==1)printf("1 %lld\n",a+1);
else if(n==2){
if(a<3)printf("-1 -1\n");
else if(a&1){
a--;
a/=2;
ll b=2*a*a+2*a;
ll c=2*a*a+2*a+1;
printf("%lld %lld\n",b,c);
}else {
a/=2;
ll b=a*a-1;
ll c=a*a+1;
printf("%lld %lld\n",b,c);
}
}
}
}
}

G - Neko’s loop

思路

首先因为k确定,也就是步数确定,那么我们就把这个换分解成gcd(n,k)个环,长度为len=n/gcd(n,k)。
然后考虑选择哪条环就可以了,一共有3种情况。
1.环的总和小于0,就是每次跑完一圈快乐值都会减少,那么我们直接取这个环上长度为min(m,len)最大值,就不需要跑完,也就是长度为m或者len的最大值即可。
2.环的总和大于0,可以跑完m/len圈且剩下的m%len长度的最大值
3.环的总和大于0,可以跑完m/len-1圈且最后获取该环的最大值
这三种情况都需要统计序列长度为x的最大连续子段和,我们使用单调队列来实现。例如当前的环是

1 5 -6 -3 4 扩充两倍 1 5 -6 -3 4 1 5 - 6 -3 4 1 5
求前缀和1 6 0 -3 4 5 10 4 1 5 6 11

然后我们用一个数组来模拟栈,假设长度为3

遍历到1,首先栈中压入{1}
遍历到2发现pre[2]=6比pre[1]=1大,则继续压入栈中{1,2},并且计算一次答案ans=max(ans,pre[i]-pre[st])
遍历到3,发现pre[3]=0,比pre[st]即栈顶小,此时我们把-6压入栈中,其他都出栈{3}
遍历到4,此时判断栈顶元素的位置i-3是否大于我们需要的长度若大于,则出战,否则继续上述的操作

根据单调队列就可以求出序列长度为x的最大连续字段和,复杂度O(n),然后我们枚举每条链g,
因为g*len=n所以这个复杂度应该就是O(n)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5+10;
int val[N];
int que[N];
ll sum[N];
int gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll solve(int n,int len)//在长度为n的序列中寻找长度为len的最大子段和
{
ll ans = 0;
int st=1,ed=0;
for(int i=1;i<=n;i++){
//如果当前的最小值与目前位置距离超过m,则出栈
while(st<=ed&&i-que[st]>len)st++;
//如果当前的值小于栈中的元素,压入栈中
while(st<=ed&&sum[que[ed]]>sum[i])ed--;
//统计答案
if(st<=ed)ans=max(ans,sum[i]-sum[que[st]]);
que[++ed]=i;
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
ll n,s,m,k;
scanf("%lld %lld %lld %lld",&n,&s,&m,&k);
for(int i=0;i<n;i++)scanf("%d",&val[i]);
int g = gcd(n,k);
int len = n/g;
int can = m/len;
//枚举链
ll ans = 0;
for(int i=0;i<g;i++){
for(int j=1,t=i;j<=2*len;j++,t=(t+k)%n)sum[j]=sum[j-1]+val[t];
//一圈<0,那么最大值肯定就是子段和
if(sum[len]<=0)ans=max(ans,solve(2*len,min(m,1ll*len)));
else//否则就是跑can圈+m取得的最大值 或者m-1圈+整段的最大值
{
ll res0 = 1ll*can*sum[len]+solve(2*len,m%len);
ans=max(ans,res0);
if(can!=0)
{
ll res1 = (can-1)*sum[len]+solve(2*len,len);
ans=max(ans,res1);
}
}
}
printf("Case #%d: %lld\n",cas,max(0ll,s-ans));
}
}

E - GuGu Convolution

思路

读完题目之后就会发现我们要求的是

如何求$b_n$?
考虑使用递推式

然后使用矩阵[A,1][B,A]就可以求出$b_n$了

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int A,B;
int mod;
struct martix{
int a[2][2];
friend martix operator * (const martix a,const martix b){
martix ret;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
ret.a[i][j]=0;
}
ll x;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
ret.a[i][j]=(ret.a[i][j]+1ll*a.a[i][k]*b.a[k][j])%mod;
return ret;
}
}ma;
martix quick_pow(martix x,ll k){
martix res;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
res.a[i][j]=i==j;
}
while(k){
if(k&1)res=res*x;
x=x*x;
k>>=1;
}
return res;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
ll n;
scanf("%d %d %lld %d",&A,&B,&n,&mod);
martix unit;
ll Bnum=B,C=1,w=1;
unit.a[0][0]=A;unit.a[0][1]=1;
unit.a[1][0]=B;unit.a[1][1]=A;
for(int i=2;1ll*i*i<=Bnum;i++){
int num = 0;
if(B%i!=0)continue;
while(B%i==0)B/=i,num++;
for(int j=0;j<num/2;j++)C*=i;
if(num&1)w*=i;
}
if(B>1)w*=B;
unit=quick_pow(unit,n);
printf("1 %d %d\n",(1ll*unit.a[0][1]*C+mod)%mod,w);
}
}
-------------你最愿意做的哪件事才是你的天赋所在-------------