蜜月日记 Part9
littlez_meow · · 休闲·娱乐
前言
争取写到 Day 100.
Day 81
数论已经好久没有在蜜月日记里出现了。
2025/01/14 [AT Xmas Contest 2019] Sum of (-1)^f(n)
题意
定义
思路
显然
可以直接 min_25,但是我不会。又懒得去推这个函数如何杜教筛。
所以考虑一些科技飞升做法,比如 PN 筛。下面会给出 PN 筛的流程,但不会有定义和证明。
首先寻找质数
然后设积性函数
则我们有
我们有
枚举因数变枚举倍数得到
设
枚举令
瓶颈在于一次杜教筛,时间复杂度
代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
const int MAXN=5e7+1,LIM=1e6+1;
bool vis[MAXN];
ll mu[MAXN];
int pr[3001135],cnt;
inline void init(){
mu[1]=1;
F(i,2,MAXN-1){
if(!vis[i]) pr[++cnt]=i,mu[i]=-1;
F(j,1,cnt){
ll k=i*pr[j];
if(k>=MAXN) break;
vis[k]=1;
if(i%pr[j]) mu[k]=-mu[i];
else break;
}
}
F(i,2,MAXN-1) mu[i]+=mu[i-1];
return;
}
__gnu_pbds::gp_hash_table<ll,int>res;
ll n,ans;
ll S(ll x){
if(x<MAXN&&x!=n) return mu[x];
if(res.find(x)!=res.end()) return res[x];
ll ans=1;
for(ll l=2,r;l<=x;l=r+1){
ll qwq=x/l;
r=x/qwq;
ans=ans-(r-l+1)*S(qwq);
}
return res[x]=ans;
}
void dfs(int step,ll now,ll H){
ll pri=pr[step],nxt=pri*pri;
if(step>cnt||now>n/nxt){
ans+=H*S(n/now);
return;
}
dfs(step+1,now,H);
for(int expo(2);now*nxt<=n;++expo,nxt*=pri) dfs(step+1,now*nxt,H*((expo&1)==0));
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
init(),S(n);
dfs(1,1,1);
cout<<ans;
return 0;
}
Day 82
快要去 WC 了,浅卷一卷。
2025/01/16 [WC2021] 斐波那契
题意
定义数列
思路
注意区分下文的
设斐波那契数列为
关于模意义下的斐波那契数列,有经典结论:
【皮萨诺周期】 模
-
若
\gcd(a,b)=1 ,则ab 的皮萨诺周期是a,b 的皮萨诺周期的最小公倍数。 -
-
对于奇质数
p\equiv 1,4\pmod 5 ,皮萨诺周期为p-1 。 -
对于奇质数
p\equiv 2,3\pmod 5 ,皮萨诺周期为2p+2 。 -
若
p 为质数,则p^k 的皮萨诺周期为p 的皮萨诺周期乘p^{k-1} 。
本题中只需要用到皮萨诺周期是
先特判
令
设
根据另一个经典结论,
再设
懒了,用的 map,时间复杂度
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
using namespace std;
const int MAXN=1e5+1;
void exgcd(int a,int b,int&x,int&y){
if(b==0) return x=1,y=0,void();
else return exgcd(b,a%b,y,x),y-=a/b*x,void();
}
ll inv(int x,int MOD){
int inv,y;
exgcd(x,MOD,inv,y);
return (inv%MOD+MOD)%MOD;
}
int N,m;
struct Triple{
int r,s,val;
bool operator<(const Triple&x)const{
return r==x.r?(s==x.s?val<x.val:s<x.s):r<x.r;
}
};
map<Triple,int>ans[MAXN];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>N>>m;
F(mm,2,m) if(m%mm==0){
int fn1=0,fn=1;
for(int n=1;;++n){
int r=__gcd(fn1,mm),s=__gcd(fn,mm),MOD=mm/r/s;
Triple qwq={r,s,fn/s*inv(fn1/r,MOD)%MOD};
if(ans[mm].find(qwq)==ans[mm].end()) ans[mm][qwq]=n;
int nxt=fn+fn1;
nxt>=mm&&(nxt-=mm);
fn1=fn,fn=nxt;
if(fn1==0&&fn==1) break;
}
}
for(;N;--N){
int a,b,mm,d;
cin>>a>>b,b=(m-b)%m;
if(a==0){
cout<<"0\n";
continue;
}
if(b==0){
cout<<"1\n";
continue;
}
d=__gcd(__gcd(a,b),m);
a/=d,b/=d,mm=m/d;
int r=__gcd(a,mm),s=__gcd(b,mm),MOD=mm/r/s;
a=a/r,b=b/s;
auto it=ans[mm].find((Triple){s,r,a*inv(b,MOD)%MOD});
if(it==ans[mm].end()) cout<<"-1\n";
else cout<<it->second<<"\n";
}
return 0;
}
Day 83
WC 讲课的题,老早就会了,一直没写。
2025/01/18 [THUPC2019] 找树
题意
定义
思路
数据范围超级小,那就应该不是传统最优化。
这个运算没有什么性质,但是众所周知的是我们可以用生成函数求边权和为某个数的生成树个数,这里同样可以。
设一条边权为
又因为 FWT 是线性变换,可以在求行列式之前对矩阵中每个元素求 FWT,再对每一位求行列式,最后 IFWT 回来。
这里
方案数可以对大质数取模。
时间复杂度
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
using namespace std;
const int MAXN=71,MAXL=4096,MOD=1e9+9,INV2=(MOD+1)/2;
inline ll qpow(ll base,int expo){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
int n,m,w,l;
char o[MAXN];
ll kir[MAXN][MAXN][MAXL];
inline void FWT(ll*poly,bool inv){
for(int len=2,i=0;len<=l;len<<=1,++i){
int qwq=len>>1;
switch(o[i]){
case '&':{
for(int i(0);i<l;i+=len) F(j,i,i+qwq-1){
if(inv) (poly[j]-=poly[j+qwq])<0&&(poly[j]+=MOD);
else (poly[j]+=poly[j+qwq])>=MOD&&(poly[j]-=MOD);
}
break;
}
case '|':{
for(int i(0);i<l;i+=len) F(j,i,i+qwq-1){
if(inv) (poly[j+qwq]-=poly[j])<0&&(poly[j+qwq]+=MOD);
else (poly[j+qwq]+=poly[j])>=MOD&&(poly[j+qwq]-=MOD);
}
break;
}
case '^':{
for(int i(0);i<l;i+=len) F(j,i,i+qwq-1){
int x=poly[j],y=poly[j+qwq];
(poly[j]=x+y)>=MOD&&(poly[j]-=MOD);
(poly[j+qwq]=x-y)<0&&(poly[j+qwq]+=MOD);
if(inv) poly[j]=poly[j]*INV2%MOD,poly[j+qwq]=poly[j+qwq]*INV2%MOD;
}
break;
}
}
}
return;
}
ll mat[MAXN][MAXN],ans[MAXL];
inline ll det(){
bool neg=0;
F(i,1,n-1){
if(!mat[i][i]){
bool flag=1;
F(j,i+1,n-1) if(mat[j][i]){
flag=0;
swap(mat[j],mat[i]);
neg^=1;
break;
}
if(flag) return 0;
}
ll inv=qpow(mat[i][i],MOD-2);
F(j,i+1,n-1){
ll factor=inv*mat[j][i]%MOD;
F(k,i,n-1) (mat[j][k]=mat[j][k]-factor*mat[i][k]%MOD)<0&&(mat[j][k]+=MOD);
}
}
ll res=(neg?MOD-1:1);
F(i,1,n-1) res=res*mat[i][i]%MOD;
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>o;
w=strlen(o),l=1<<w;
F(i,1,m){
int x,y,v;
cin>>x>>y>>v;
--kir[x][y][v],--kir[y][x][v];
++kir[x][x][v],++kir[y][y][v];
}
F(x,1,n-1) F(y,1,n-1){
F(v,0,l-1) kir[x][y][v]<0&&(kir[x][y][v]+=MOD);
FWT(kir[x][y],0);
}
F(i,0,l-1){
F(x,1,n-1) F(y,1,n-1) mat[x][y]=kir[x][y][i];
ans[i]=det();
}
FWT(ans,1);
R(i,l-1,0) if(ans[i]) return cout<<i,0;
cout<<"-1";
return 0;
}
Day 84
考 WC 前紧急打一打多项式板子。
2025/01/19 [ABC385G] Counting Buildings
题意
求长为
思路
把排列从最大值处分开,右侧没有前缀最大值,左侧没有后缀最大值。
设前后缀缀最大值分别有
右边同理,是
问题变成求一行第一类斯特林数。众所周知,第
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define vi vector<int>
using namespace std;
const int MAXN=(1<<19)+5,MOD=998244353,G=3;
inline ll qpow(ll base,int expo){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
const int INVG=qpow(G,MOD-2);
ll powg[2][20][MAXN];
inline void init(){
F(i,0,1) F(j,1,19){
powg[i][j][0]=1;
ll qwq=qpow(i?INVG:G,(MOD-1)>>j);
F(k,1,MAXN-1) powg[i][j][k]=powg[i][j][k-1]*qwq%MOD;
}
return;
}
int rev[MAXN];
inline void NTT(int*poly,int len,bool inv){
F(i,0,len-1) if(i<rev[i]) swap(poly[i],poly[rev[i]]);
for(int i=1,expo=1;i<len;i<<=1,++expo) for(int j=0;j<len;j+=(i<<1)) F(k,0,i-1){
int&x(poly[j|k]),&y(poly[i|j|k]);
ll qwq=y*powg[inv][expo][k]%MOD;
(y=x-qwq)<0&&(y+=MOD);
(x+=qwq)>=MOD&&(x-=MOD);
}
if(inv){
ll invl=qpow(len,MOD-2);
F(i,0,len-1) poly[i]=poly[i]*invl%MOD;
}
return;
}
int px[MAXN],py[MAXN];
inline vi mul(const vi&x,const vi&y){
int n=x.size()+y.size()-1,l=1;
while(l<n) l<<=1;
F(i,0,l-1) rev[i]=(rev[i>>1]>>1)|((i&1)?(l>>1):0);
F(i,0,x.size()-1) px[i]=x[i];
F(i,x.size(),l-1) px[i]=0;
F(i,0,y.size()-1) py[i]=y[i];
F(i,y.size(),l-1) py[i]=0;
NTT(px,l,0),NTT(py,l,0);
F(i,0,l-1) px[i]=px[i]*1ll*py[i]%MOD;
NTT(px,l,1);
vi res(n);
F(i,0,n-1) res[i]=px[i];
return res;
}
vi solve(int l,int r){
if(l>r) return vi({1});
if(l==r) return vi({l,1});
int mid=(l+r)>>1;
return mul(solve(l,mid),solve(mid+1,r));
}
int n,k;
vi s;
ll fact[MAXN],inv[MAXN];
inline ll C(int x,int y){
return x>=y&&y>=0?fact[x]*inv[y]%MOD*inv[x-y]%MOD:0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
init();
cin>>n>>k;
s=solve(0,n-2);
fact[0]=1;
F(i,1,n) fact[i]=fact[i-1]*i%MOD;
inv[n]=qpow(fact[n],MOD-2);
R(i,n,1) inv[i-1]=inv[i]*i%MOD;
ll ans=0;
F(y,1,n){
int x=y+k;
if(x<=0) continue;
if(x+y-2>n-1) break;
ans=(ans+s[x+y-2]*C(x+y-2,x-1))%MOD;
}
cout<<ans;
return 0;
}
Day 85
被 WC T2 位运算创飞了。感觉自己不会位运算相关 trick,尤其是既有异或又有加法的。
2025/01/21 [HNOI/AHOI2018] 寻宝游戏
题意
给定
思路
首先这个肯定是按位考虑。下面暂时认为
先研究
发现这个过程很像字典序比较。把按位或当成
然后是
时间复杂度
代码
#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define fi first
#define se second
using namespace std;
const int MAXN=5002,MOD=1e9+7;
int n,m,q,val[MAXN];
pair<string,int>id[MAXN];
char ch[MAXN][MAXN],ask[MAXN];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>q;
F(i,1,n) cin>>(ch[i]+1);
F(i,1,m){
R(j,n,1) id[i].fi+=ch[j][i],val[i]=((val[i]<<1)|(ch[j][i]^'0'))%MOD;
id[i].se=i;
}
sort(id+1,id+m+1);
F(i,1,n) id[0].fi+='0',id[m+1].fi+='1',val[m+1]=((val[m+1]<<1)|1)%MOD;
id[0].se=0,id[m+1].se=m+1,val[0]=0,++val[m+1];
for(;q;--q){
cin>>(ask+1);
int l=m+1,r=0;
F(i,1,m){
if(ask[id[i].se]=='0') r=max(i,r);
else l=min(i,l);
}
if(l<r) cout<<"0\n";
else cout<<(val[id[l].se]-val[id[r].se]+MOD)%MOD<<"\n";
}
return 0;
}
Day 86
寒假摆会烂,写点典题。
2025/01/25 无标号无根树计数
题意
求
思路
首先求出
设
在无标号体系下的
【欧拉变换】
第一个等号的组合意义是背包,第二个等号是先求
于是有生成函数方程:
下面就是要化简成好处理的方式了。先展开成
两边同时求导得到
使用链式求导法则,得到
整理得到
两边同时乘
设
整理得到:
其中
这个式子就可以分治 FFT 了。注意
然后容斥掉根不是重心的方案数。
当
否则,还要减去以两个重心为根不同构的情况
最后答案为:
瓶颈在于分治 FFT,时间复杂度
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
const int MAXN=(1<<19)+5,MOD=998244353,G=3;
inline ll qpow(ll base,int expo){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
base=base*base%MOD,expo>>=1;
}
return res;
}
const int INVG=qpow(G,MOD-2);
ll powg[2][20][MAXN];
inline void init(){
F(i,0,1) F(j,1,19){
powg[i][j][0]=1;
ll qwq=qpow(i?INVG:G,(MOD-1)>>j);
F(k,1,MAXN-1) powg[i][j][k]=powg[i][j][k-1]*qwq%MOD;
}
return;
}
int rev[MAXN];
inline void NTT(int*poly,int len,bool inv){
F(i,0,len-1) if(i<rev[i]) swap(poly[i],poly[rev[i]]);
for(int i=1,expo=1;i<len;i<<=1,++expo) for(int j=0;j<len;j+=(i<<1)) F(k,0,i-1){
int&x(poly[j|k]),&y(poly[i|j|k]);
ll qwq=y*powg[inv][expo][k]%MOD;
(y=x-qwq)<0&&(y+=MOD);
(x+=qwq)>=MOD&&(x-=MOD);
}
if(inv){
ll invl=qpow(len,MOD-2);
F(i,0,len-1) poly[i]=poly[i]*invl%MOD;
}
return;
}
inline int mul(int l,int*x,int*y,int*res){
l=1<<(__lg(l)+1);
F(i,0,l-1) rev[i]=(rev[i>>1]>>1)|((i&1)?(l>>1):0);
static int px[MAXN],py[MAXN];
memcpy(px,x,sizeof(int)*l);
memcpy(py,y,sizeof(int)*l);
NTT(px,l,0),NTT(py,l,0);
F(i,0,l-1) res[i]=px[i]*1ll*py[i]%MOD,px[i]=py[i]=0;
NTT(res,l,1);
return l;
}
int n,f[MAXN],g[MAXN];
void DandQ_FFT(int l,int r){
int mid=(l+r)>>1;
if(l==r){
if(l==1) f[l]=1;
else f[l]=f[l]*qpow(l-1,MOD-2)%MOD;
int qwq=f[l]*1ll*l%MOD;
for(int i=l;i<=n;i+=l) (g[i]+=qwq)>=MOD&&(g[i]-=MOD);
return;
}
DandQ_FFT(l,mid);
if(l==1){
static int p1[MAXN],p2[MAXN],res[MAXN];
F(i,l,mid) p1[i-l]=f[i],p2[i-l]=g[i];
int len=mul((mid-l)<<1,p1,p2,res);
F(i,mid+1,r) (f[i]+=res[i-l-1])>=MOD&&(f[i]-=MOD);
F(i,0,len) p1[i]=p2[i]=res[i]=0;
}else{
static int p1[MAXN],p2[MAXN],res[MAXN];
F(i,l,mid) p1[i-l]=f[i];
F(i,1,r-l) p2[i-1]=g[i];
int len=mul(r+mid-l-l+1,p1,p2,res);
F(i,mid+1,r) (f[i]+=res[i-l-1])>=MOD&&(f[i]-=MOD);
F(i,0,len) p1[i]=p2[i]=res[i]=0;
F(i,l,mid) p1[i-l]=g[i];
F(i,1,r-l) p2[i-1]=f[i];
len=mul(r-l+mid-l+1,p1,p2,res);
F(i,mid+1,r) (f[i]+=res[i-l-1])>=MOD&&(f[i]-=MOD);
F(i,0,len) p1[i]=p2[i]=res[i]=0;
}
DandQ_FFT(mid+1,r);
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
init();
cin>>n;
DandQ_FFT(1,n);
ll ans=f[n];
F(i,(n>>1)+1,n-1) ans=(ans-f[i]*1ll*f[n-i])%MOD;
if(!(n&1)) ans=(ans-f[n>>1]*(f[n>>1]-1ll)/2)%MOD;
ans<0&&(ans+=MOD);
cout<<ans;
return 0;
}
Day 87
很神秘的题目。
2025/01/26 [ARC055D] 隠された等差数列
题意
给定一个长为
思路
先从无解情况开始思考。发现
然后只有一种取值也是好处理的,此时这种取值必然是
接下来我们假设
首先你会发现
我们设
然后你发现
于是我们就有不等式组
从小到大枚举
问题转化为给定平面上若干线段
发现若
记点集
进一步的,你发现只有
求出凸包后,斜率具有单调性,双指针的时间复杂度
官方题解说
答案的上界是
总的时间复杂度是
好吧,最后懒了,考虑到题目年代久远,
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
const int MAXN=1e4+2;
const double EPS=1e-9;
char s[MAXN];
int n,d[MAXN],l[MAXN];
set<int>delta;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>s;
n=strlen(s);
F(i,0,n-1) d[i]=s[i]-'0';
F(i,0,n-2) delta.insert((d[i+1]-d[i]+10)%10);
if(delta.size()>2) return cout<<"-1",0;
if(delta.size()==1||n==1) return cout<<d[0],0;
int x=*delta.begin(),y=*delta.rbegin();
if((x-y+10)%10!=1&&(y-x+10)%10!=1) return cout<<"-1",0;
l[0]=d[0];
if((y-x+10)%10==9) x=9,y=10;
F(i,1,n-1){
if((l[i-1]+x)%10==d[i]) l[i]=l[i-1]+x;
else l[i]=l[i-1]+y;
}
double bl=0,br=1e9;
F(i,0,n-1) F(j,i+1,n-1){
bl=max(bl,(l[j]-l[i]-1.0+EPS)/(j-i));
br=min(br,(l[j]+1.0-l[i]-EPS)/(j-i));
}
if(bl>br) return cout<<"-1",0;
for(int i=1;;i*=10) if(ceil(bl*i)<=floor(br*i)){
int b=floor(br*i),res=0;
F(j,0,n-1) res=max(res,l[j]*i-j*b);
return cout<<res,0;
}
return 0;
}#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
const int MAXN=1e4+2;
const double EPS=1e-9;
char s[MAXN];
int n,d[MAXN],l[MAXN];
set<int>delta;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>s;
n=strlen(s);
F(i,0,n-1) d[i]=s[i]-'0';
F(i,0,n-2) delta.insert((d[i+1]-d[i]+10)%10);
if(delta.size()>2) return cout<<"-1",0;
if(delta.size()==1||n==1) return cout<<d[0],0;
int x=*delta.begin(),y=*delta.rbegin();
if((x-y+10)%10!=1&&(y-x+10)%10!=1) return cout<<"-1",0;
l[0]=d[0];
if((y-x+10)%10==9) x=9,y=10;
F(i,1,n-1){
if((l[i-1]+x)%10==d[i]) l[i]=l[i-1]+x;
else l[i]=l[i-1]+y;
}
double bl=0,br=1e9;
F(i,0,n-1) F(j,i+1,n-1){
bl=max(bl,(l[j]-l[i]-1.0+EPS)/(j-i));
br=min(br,(l[j]+1.0-l[i]-EPS)/(j-i));
}
if(bl>br) return cout<<"-1",0;
for(int i=1;;i*=10) if(ceil(bl*i)<=floor(br*i)){
int b=floor(br*i),res=0;
F(j,0,n-1) res=max(res,l[j]*i-j*b);
return cout<<res,0;
}
return 0;
}
Day 88
考查选手补题能力。
2025/01/27 [ARC166E] Fizz Buzz Difference
题意
所以
接下来是最小化
回到题目本身的限制,我们要有
如果
否则,化简不等式得到
这是经典问题。设
如果
否则我们设
由于
时间复杂度同辗转相除为
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
ll n,a,b;
ll g(ll u,ll v,ll t,ll m){
if(u==0) return 0;
if(((u-1)/t+1)*t<=v) return (u-1)/t+1;
ll s=g((t-v%t)%t,(t-u%t)%t,m%t,t);
return (s*m+u-1)/t+1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;
for(cin>>T;T;--T){
cin>>n>>a>>b;
ll ql=0,qr=1e12,d=__gcd(a,b);
while(ql<qr){
ll mid((ql+qr)>>1);
if(mid-2-(mid*a-d-1)/b<=n) ql=mid+1;
else qr=mid;
}
ll len=ql-1,p,q;
if(len*a-2-b*(len-2-n)>=b) p=0;
else p=g(((1-len*a)%b+b)%b,b-1,a,b);
q=p+len;
cout<<p*a+1<<" "<<q*a-1<<"\n";
}
return 0;
}
Day 89
春晚太无聊了,来看看很久远的题目,完成五年前的约定。
2025/01/29 [MtOI2019] 幽灵乐团 / 莫比乌斯反演基础练习题
题意
写出所求式
交换求积顺序
即有:
预处理阶乘可以做到单次
t=0 ,求 h
写出所求式
不妨假设
整理,枚举
化简一下有
指数上是这道题,化简过程不再重复。结果是
枚举
括号里的东西可以
t=1 ,求 g
写出所求式
即
等差数列求和随便化简一下得到
即有:
写出所求式
即
不妨设
然后枚举
写出所求式
化简有
进行欧拉反演,有
枚举因数变为枚举倍数
再来一次变成
展开得到
即有:
可以
t=2 ,求 h
大的要来了。
写出所求式
不妨设
枚举
化简一下,有
指数的后两个求和是经典问题,上面已经提到过,代入得
对后一个求和号进行欧拉反演,有
枚举因数变枚举倍数有
化简得到
可惜的是,括号里的式子没法预处理,我们必须继续化简。枚举因数变枚举倍数得
分成
先处理右边的括号。
枚举
代入
再处理左边的括号。枚举
左边括号可以做到
然后你发现
总时间复杂度是
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
#define ll long long
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
using namespace std;
const int MAXN=1e5+1;
int MOD;
inline ll qpow(ll base,ll expo){
ll res(1);
while(expo){
(expo&1)&&(res=res*base%MOD);
expo>>=1,base=base*base%MOD;
}
return res;
}
bool vis[MAXN];
int pr[MAXN],cnt,mu[MAXN],phi[MAXN];
ll fact[MAXN],sum[MAXN],iimul[MAXN],dmu[MAXN],m1[MAXN],m2[MAXN],im1[MAXN],im2[MAXN];
inline void init(){
mu[1]=phi[1]=1;
F(i,2,MAXN-1){
if(!vis[i]) pr[++cnt]=i,mu[i]=-1,phi[i]=i-1;
F(j,1,cnt){
ll k=i*pr[j];
if(k>=MAXN) break;
vis[k]=1;
if(i%pr[j]) mu[k]=-mu[i],phi[k]=phi[i]*(pr[j]-1);
else{
phi[k]=phi[i]*pr[j];
break;
}
}
}
fact[0]=iimul[0]=1;
F(i,1,MAXN-1) dmu[i]=1,sum[i]=sum[i-1]+phi[i],fact[i]=fact[i-1]*i%MOD,iimul[i]=iimul[i-1]*qpow(i,i)%MOD;
F(i,1,MAXN-1) if(mu[i]) for(int j=i,k=1;j<MAXN;j+=i,++k) dmu[j]=dmu[j]*(mu[i]==1?k:qpow(k,MOD-2))%MOD;
m1[0]=m2[0]=1;
F(i,1,MAXN-1) m1[i]=m1[i-1]*dmu[i]%MOD,m2[i]=m2[i-1]*qpow(dmu[i],i*1ll*i%(MOD-1))%MOD;
F(i,0,MAXN-1) im1[i]=qpow(m1[i],MOD-2),im2[i]=qpow(m2[i],MOD-2);
return;
}
#define C2(x) ((x+1ll)*x/2%(MOD-1))
inline ll g0(int a,int b,int c){
return qpow(fact[a],b*1ll*c%(MOD-1));
}
inline ll h0(int a,int b,int c){
ll res=1;
if(a>b) swap(a,b);
for(int l=1,r;l<=a;l=r+1){
ll ta=a/l,tb=b/l;
r=min(a/ta,b/tb);
res=res*qpow(m1[r]*im1[l-1]%MOD,ta*tb%(MOD-1))%MOD;
}
return qpow(res,c);
}
inline ll g1(int a,int b,int c){
return qpow(iimul[a],C2(b)*C2(c)%(MOD-1));
}
inline ll h1(int a,int b,int c){
ll res=1;
if(a>b) swap(a,b);
for(int l=1,r;l<=a;l=r+1){
ll ta=a/l,tb=b/l;
r=min(a/ta,b/tb);
res=res*qpow(m2[r]*im2[l-1]%MOD,C2(ta)*C2(tb)%(MOD-1))%MOD;
}
return qpow(res,C2(c)%(MOD-1));
}
inline ll g2(int a,int b,int c){
ll res=1;
int lim=min(a,min(b,c));
for(int l=1,r;l<=lim;l=r+1){
ll ta=a/l,tb=b/l,tc=c/l;
r=min(a/ta,min(b/tb,c/tc));
res=res*qpow(fact[ta],(sum[r]-sum[l-1])%(MOD-1)*tb%(MOD-1)*tc%(MOD-1))%MOD;
}
return res;
}
inline ll h2(int a,int b,int c){
ll res=1;
int lim=min(a,min(b,c));
for(int l=1,r;l<=lim;l=r+1){
ll ta=a/l,tb=b/l,tc=c/l;
r=min(a/ta,min(b/tb,c/tc));
res=res*h0(ta,tb,(sum[r]-sum[l-1])%(MOD-1)*tc%(MOD-1))%MOD;
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;
cin>>T>>MOD;
for(init();T;--T){
int A,B,C;
cin>>A>>B>>C;
cout<<g0(A,B,C)*g0(B,A,C)%MOD*qpow(h0(A,B,C)*h0(A,C,B)%MOD,MOD-2)%MOD<<" ";
cout<<g1(A,B,C)*g1(B,A,C)%MOD*qpow(h1(A,B,C)*h1(A,C,B)%MOD,MOD-2)%MOD<<" ";
cout<<g2(A,B,C)*g2(B,A,C)%MOD*qpow(h2(A,B,C)*h2(A,C,B)%MOD,MOD-2)%MOD<<"\n";
}
return 0;
}
Day 90
一坨题目。
2025/01/30~2025/01/31 [ICPC 2018 Qingdao R] Sequence and Sequence
题意
定义序列
思路
不妨令
把递推式展开得到
既然是求前缀和,我们可以使用有限微积分。
先写成有限微积分的形式
类比微积分的分部积分,对于有限微积分,我们有:
【分部求和法】
对上面的定和式使用分部求和,得到
展开得到
把
你发现大部分情况
简单化简得到
于是设
则有: