[题解] Atcoder Beginner Contest ABC 270 G Ex 题解

点我看题

G - Sequence in mod P

稍微观察一下就会发现,进行x次操作后的结果是AxS+(1++Ax1)B。如果没有右边那一坨关于B的东西,那我们要求的就是满足AxGS的最小的x(离散对数)。有一个叫BSGS的东西是专门干这个的

考虑能不能把这个式子直接化成AxB的形式。先把答案=0、A=0和A=1的情况特判掉,因为会影响后面推式子。

(1)AxS+(1++Ax1)BG(2)AxS+Ax1A1BG()(3)AxS+AxA1BG+1A1B(4)Ax(S+BA1)G+1A1B(5)AxG+BA1S+BA1

进行上面的最后一步之前判断S+BA1是否为0,如果是0的话答案就是1,因为之前已经特判过答案为0的情况了;否则就直接跑BSGS就行了。

边界很多,写的时候注意一点。

时间复杂度O(tP)

点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
#define MOD P
using namespace std;
LL t,P,A,B,S,G,lim;
map <LL,LL> mp;
LL qpow(LL x,LL a)
{
LL res=x,ret=1;
while(a>0)
{
if((a&1)==1) ret=ret*res%MOD;
a>>=1;
res=res*res%MOD;
}
return ret;
}
int main()
{
cin>>t;
rep(tn,t)
{
cin>>P>>A>>B>>S>>G;
if(S==G)
{
puts("0");
continue;
}
if(A==0)
{
if(B==G) puts("1");
else puts("-1");
continue;
}
if(A==1)
{
if(B==0)
{
puts("-1");
continue;
}
LL tmp=(G-S+P)%P;(tmp*=qpow(B,P-2))%=P;
cout<<tmp<<endl;
continue;
}
LL right=(G+B*qpow(A-1,P-2))%P,mul=(S+B*qpow(A-1,P-2))%P;
if(mul==0)
{
if(right==0)
{
puts("1");
continue;
}
puts("-1");
continue;
}
(right*=qpow(mul,P-2))%=P;
//A^ans = right
//bsgs
lim=ceil(sqrt((double)P));
mp.clear();
LL cur=1;
rep(i,lim)
{
LL res=right*cur%P;
if(mp.find(res)==mp.end()) mp[res]=i;
else mp[res]=max(mp[res],(LL)i);
(cur*=A)%=P;
}
LL bas=1;rep(i,lim) (bas*=A)%=MOD;
cur=bas;
LL ans=1e18;
repn(a,lim)
{
if(mp.find(cur)!=mp.end())
{
LL curres=(LL)a*lim-mp[cur];
if(curres<0) curres+=P-1;
curres%=(P-1);
ans=min(ans,curres);
}
(cur*=bas)%=P;
}
if(ans==1e18) puts("-1");
else cout<<ans<<endl;
}
return 0;
}

Ex - add 1

官方题解精简版,供懒人食用。

注意到输入的a数组是单调不降的。令当前所有counter的值分别为c1,c2cn(下标从1开始)。定义一个c1cn的状态的"值"为maxi=1n{max(0,aici)}。容易发现:这个值在[0,an]中,值为0的时候我们就达到目标了,并且一次操作只能让这个值最多减少1。考虑一个值为k的状态(k>0)经过恰好1次操作会怎么变化。首先由于a1=0,an>0,所以我们肯定可以找到唯一的r,满足ar<kar+1。现在开始进行操作,我们随机选择一个i把ci归零。有两种情况:

  • irai<k,所以任意一个ci被归零都不会导致状态的新值达到或超过k。所以状态的值减一。
  • i>r。很显然状态的值会变成ai

所以可以发现所有值相等的状态都是"等价"的,至少它们达到目标的期望步数一定是相等的。令dpi表示值为i的状态达到目标的期望步数。dp0=0,我们要求dpan。转移比较显然,是这样的:

(6)dpi=1n(ridpi1+j=ri+1ndpaj)+1(7)dpi1=1ri(n(dpi1)j=ri+1ndpaj)

其中ri就是上面说的每个状态值对应的r。

其实这样已经可做了,但是毕竟我们要求的是dpan,但这个式子是从后往前转移的,但我们要求dpan,不太方便,所以可以转化一下:令fi=dpandpi,把dpi=dpanfi带入上面的式子,并再把式子两边同时用dpan减。

(8)dpandpi1=dpan1ri(n(dpanfi1)dpan(nri)+j=ri+1nfaj)      dpan(9)fi1=1ri(n(fi+1)j=ri+1nfaj)

发现可以把[0,an]这个区间分成n-1段处理,分别是[a0,a11],[a1,a21]每段内部的dp转移都相同(因为ri+1相同)。所以对每一段分别矩阵快速幂转移就可以了。官方题解还有复杂度更低一点的优化,但是这样已经可以过了,稍微有一点点卡常而已。

时间复杂度O(23nlog)

点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
const LL MOD=998244353;
LL qpow(LL x,LL a)
{
LL res=x,ret=1;
while(a>0)
{
if((a&1)==1) ret=ret*res%MOD;
a>>=1;
res=res*res%MOD;
}
return ret;
}
LL n,a[200010],y[200010];
vector <vector <LL> > pw[70];
vector <vector <LL> > mul(vector <vector <LL> > a,vector <vector <LL> > b)
{
vector <vector <LL> > ret={{0,0},{0,0}};
rep(i,2) rep(j,2) rep(k,2) (ret[i][j]+=a[i][k]*b[k][j])%=MOD;
return ret;
}
int main()
{
cin>>n;
repn(i,n) scanf("%lld",&a[i]);
y[n]=0;
LL sum=0;
for(int i=n-1;i>0;--i)
{
if(a[i]==a[i+1])
{
y[i]=y[i+1];
(sum+=y[i])%=MOD;
continue;
}
LL r=i,add=(n*qpow(r,MOD-2)+(MOD-sum*qpow(r,MOD-2)%MOD))%MOD;
pw[0]={{n*qpow(r,MOD-2)%MOD,1},{0,1}};
vector <vector <LL> > res(0);
LL dist=a[i+1]-a[i],cc=0;
while(dist>0)
{
if(dist&1)
{
if(res.empty()) res=pw[cc];
else res=mul(res,pw[cc]);
}
dist>>=1;++cc;
if(dist==0) break;
pw[cc]=mul(pw[cc-1],pw[cc-1]);
}
y[i]=(res[0][0]*y[i+1]+res[0][1]*add)%MOD;
(sum+=y[i])%=MOD;
}
cout<<y[1]<<endl;
return 0;
}
posted @   LegendStane  阅读(145)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示