20260825sol
20260825sol
题面链接: https://yun.139.com/shareweb/#/w/i/2xop1P8DQ5Bw1 提取码:b1xd
上午
T1 log
模板BSGS
#include <bits/stdc++.h>
#define int long long
using namespace std;
bool bsgs(int a,int b,int p){
unordered_map <int,int> mp;
int s = sqrt(p)+1 , as = 1;
for(int i = 1 ; i<=s ; i++) as = as*a%p;
for(int k = 0 , tmp = b; k<=s ; k++,tmp=tmp*a%p)
mp[tmp]=k;
for(int t = 1 , tmp = as; t<=s ; t++,tmp=tmp*as%p)
if(mp.count(tmp)){
printf("%lld\n",t*s-mp[tmp]);
return 1;
}
return 0;
}
signed main(){
freopen("log.in","r",stdin);
freopen("log.out","w",stdout);
int n;scanf("%lld",&n);
while(n--){
int a,b,p;scanf("%lld%lld%lld",&a,&b,&p);
if(!bsgs(a,b,p)) puts("No solution");
}
return 0;
}
T2 lucky
设\(dp_{i,j}\)为考虑长度为\(i\)的数列中未出现过讨厌串且末尾有\(j\)位与讨厌字符串匹配的方案数。转移即枚举\(i,j\)和下一位是\(0-9\)去计算新的匹配长度向后刷表即可,注意\(dp_{i,m}\)是不合法不能从此转移出去的。发现转移是与\(i\)无关的,故构造一个转移矩阵矩阵快速幂即可。
讲一下怎么构造矩阵,及要求一个\(A\)矩阵满足下式
\(A_{i,j}\)即表示末尾匹配数量为\(j\)转移到末尾匹配数量为\(i\)的系数。那我们枚举\(j\),暴力算出后面分别加上\(0-9\)的匹配长度\(k\),则\(A_{k,j}\)增加1,具体实现我直接写了一个可持久化KMP算匹配长度,但好像暴力算匹配长度也行。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int M = 35;
int p;
struct Mar{
int n,m,a[M][M];
Mar(int dn=0,int dm=0) : n(dn),m(dm){
memset(a,0,sizeof(a));
}
Mar operator * (const Mar &b) const&{
Mar c(n,b.m);
for(int i = 0 ; i<=n ; i++)
for(int j = 0 ; j<=b.m ; j++)
for(int k = 0 ; k<=m ; k++)
c.a[i][j] = (c.a[i][j]+a[i][k]*b.a[k][j])%p;
return c;
}
};
Mar qpow(Mar a,int b){
Mar ret(a.n,a.m);
for(int i = 0 ; i<=ret.n ; i++) ret.a[i][i]=1;
while(b){
if(b&1) ret = ret*a;
b>>=1 , a = a*a;
}
return ret;
}
int n,m,nxt[M][15],len[M];
string s;
signed main(){
scanf("%lld%lld%lld",&n,&m,&p);
cin>>s;s=" "+s;
for(int i = 0 ; i<10 ; i++) nxt[0][i]=-1;
for(int i = 1 ; i<=m ; i++){
len[i]=nxt[i-1][s[i]-'0']+1;
memcpy(nxt[i],nxt[len[i]],sizeof(nxt[i]));
nxt[i][s[len[i]+1]-'0']=len[i];
}
Mar bas(m,m),ans(m,1);
for(int i = 0 ; i<m ; i++){
for(int j = 0 ; j<10 ; j++){
if(s[i+1]-'0'!=j) bas.a[nxt[i][j]+1][i]++;
else bas.a[i+1][i]++;
}
}
ans.a[0][0]=1;
ans = qpow(bas,n)*ans;
int res = 0;
for(int i = 0 ; i<m ; i++) res = (res+ans.a[i][0])%p;
printf("%lld",res);
return 0;
}
T3 creature
首先有裴蜀定理,能到达任意位置即所有刻印值互质,讲两种做法。
第一种即考虑限制为\(\gcd(a_1,a_2,a_3,...,a_n,m)=1\),改为莫比乌斯反演的形式,即答案为
\( \begin{aligned} ans&=\sum_{i=1}^{m}...\sum_{i=1}^{m}\sum_{d|\gcd(a_1,a_2,...,a_n,m)}\mu(d) \\&=\sum_{i=1}^{m}...\sum_{i=1}^{m}\sum_{d|a_1且d|a_2...且d|m}\mu(d) \\&=\sum_{d|m}\mu(d)\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor }...\sum_{i=1}^{\left \lfloor \frac{m}{d} \right \rfloor }1 \\&=\sum_{d|m}\mu(d)\left \lfloor \frac{m}{d} \right \rfloor ^n \end{aligned}\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int p = 1e9+7;
int qpow(int a,int b){
int ret = 1;
while(b){
if(b&1) ret = ret*a%p;
b>>=1 , a = a*a%p;
}
return ret;
}
int mu(int x){
int cnt = 0;
for(int i = 2 ; i*i<=x ; i++){
if(x%i==0){
++cnt,x/=i;
if(x%i==0) return 0;
}
}
if(x>1) ++cnt;
return cnt&1?-1:1;
}
int calc(int n,int m){
int ret = 0;
for(int i = 1 ; i*i<=m ; i++){
if(m%i==0){
ret = (ret+mu(i)*qpow(m/i,n)%p)%p;
if(i*i!=m) ret = (ret+mu(m/i)*qpow(m/(m/i),n)%p)%p;
}
}
return (ret+p)%p;
}
int n,m;
signed main(){
scanf("%lld%lld",&n,&m);
printf("%lld",calc(n,m));
return 0;
}
第二种方法就是容斥,设序列的\(\gcd\)为\(d\)。值\(m\)的质因子为\(p_1,p_2...\),考虑到答案就是\(m^n-(满足p1|d的序列 \cup 满足p2|d的序列 \cup 满足p3|d的序列...)\) , 若干个集合的交集就是\(\left \lfloor \frac{m}{\gcd(p_{a_1},p_{a_2},...)} \right \rfloor ^ n\),\(a\)为所选的质因子下标,容斥即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int p = 1e9+7;
int sub(int a,int b){ return a<b ? a+p-b : a-b;}
int add(int a,int b){ return a+b>=p?a+b-p:a+b;}
int pri[15],cnt,n,m,ans;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int qpow(int a,int b){
int ret = 1;
while(b){
if(b&1) ret = ret*a%p;
b>>=1 , a = a*a%p;
}
return ret;
}
void init(int m){
for(int i = 2 ; i*i<=m ; i++){
if(m%i==0){
pri[++cnt]=i;
while(m%i==0) m/=i;
}
}
if(m>1) pri[++cnt]=m;
return ;
}
void dfs(int step,int gcd,int tot){
if(step>cnt){
if(gcd==1) return ;
if(tot&1) ans=sub(ans,qpow(m/gcd,n));
else ans=add(ans,qpow(m/gcd,n));
return ;
}
dfs(step+1,gcd*pri[step],tot+1);
dfs(step+1,gcd,tot);
return ;
}
signed main(){
scanf("%lld%lld",&n,&m);
init(m);
ans=qpow(m,n),dfs(1,1,0);
printf("%lld",ans);
return 0;
}
T4 remainder
模板数论分块,讲一下基础知识吧
就首先\(\left \lfloor \frac{n}{i} \right \rfloor (1\le i\le n)\)只有\(n\)中取值。因为对于\(i \le \sqrt n\) , \(\left \lfloor \frac{n}{i} \right \rfloor\)最多只有\(\sqrt n\)种取值,对于\(i \ge \sqrt n\) , \(\left \lfloor \frac{n}{i} \right \rfloor\)最多也只有\(\sqrt n\)种取值。然后每种取值是连续的一段是显然的。考虑如何确定每一段的\(l\)和\(r\),首先\(l\)为上一段的\(r+1\)。设\(k=\left \lfloor \frac{n}{l} \right \rfloor\)所以我们需要找到一个最大的\(r\),满足\(k \le \frac{n}{r} < k+1\),倒过来有\(\frac{1}{k+1} < \frac{r}{n} \le\frac{1}{k}\),所以\(\frac{n}{k+1} < r \le \frac{n}{k}\) , 故$r=\left \lfloor \frac{n}{\left \lfloor \frac{n}{l} \right \rfloor } \right \rfloor $
回到这道题,只需要知道\(\sum_{i=1}^n k \bmod i = \sum_{i=1}^n k - i\left \lfloor \frac{k}{i} \right \rfloor = kn - \sum_{i=1}^n i\left \lfloor \frac{k}{i} \right \rfloor\) , 然后数论分块加个等比数列求和即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,k,ans;
signed main(){
freopen("remainder.in","r",stdin);
freopen("remainder.out","w",stdout);
scanf("%lld%lld",&n,&k);
ans = k*n;n=min(n,k);
for(int l=1,r=0,q=0;l<=n;l=r+1){
q = k/l , r = min(n,k/q);
ans-=(r-l+1)*(l+r)/2*q;
}
printf("%lld",ans);
return 0;
}
下午
T1 equation
插板法模板,需要注意的取模最后要用负数取模
#include <bits/stdc++.h>
#define int long long
using namespace std;
void exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return ;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
int binom(int a,int b,int p){
int s1 = 1 , s2 = 1;
for(int i = a-b+1 ; i<=a ; i++) s1 = s1*i%p;
for(int i = 1 ; i<=b ; i++) s2 = s2*i%p;
int x=0,y=0;exgcd(s2,p,x,y);
return (s1*x%p+p)%p;
}
signed main(){
// freopen("eqution.in","r",stdin);
// freopen("eqution.out","w",stdout);
int t;scanf("%lld",&t);
while(t--){
int n,m,mod;scanf("%lld%lld%lld",&n,&m,&mod);
printf("%lld\n",binom(m+n-1,n-1,mod));
}
return 0;
}
T2 power
模板拓展欧拉定理
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int phi = 281397888 , p = 1e9+8;
int qpow(int a,int b){
int ret = 1;
while(b){
if(b&1) ret = ret*a%p;
b>>=1 , a = a*a%p;
}
return ret;
}
int rd(){
int w = 0 , f = 0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while('0'<=ch&&ch<='9'){
w = (w<<3)+(w<<1)+(ch^48),ch=getchar();
if(w>=phi) f = 1 , w%=phi;
}
if(f) w+=phi;
return w;
}
signed main(){
freopen("power.in","r",stdin);
freopen("power.out","w",stdout);
printf("%lld",qpow(3,rd()));
return 0;
}
T3 comb
转换成同余式就是求\(\binom{n}{i} \equiv 0 \pmod p\) , 用\(Lucas\)就有\(\binom{n}{i} \equiv \binom{n_1}{m_1}\binom{n_2}{m_2}...\binom{n_s}{m_s}\pmod p (n_i<p)\),即在\(p\)进制下的表示。那不为0的数就有\((n_1+1)(n_2+1)...(n_s+1)\)用\(n+1\)减去\((n_1+1)(n_2+1)...(n_s+1)\)就行。
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int n,p,ans=1;scanf("%lld%lld",&n,&p);
for(int i = n ; i ; i/=p) ans*=(i%p+1);
printf("%lld",n-ans);
return 0;
}
T4 derange
模板错排,递推或者二项式反演。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int p = 1e9+7 , N = 1e6+15;
int n,f[N];
signed main(){
freopen("derange.in","r",stdin);
freopen("derange.out","w",stdout);
scanf("%lld",&n);f[2]=1;
for(int i = 3 ; i<=n ; i++) f[i] = (i-1)*(f[i-1]+f[i-2])%p;
printf("%lld",f[n]);
return 0;
}
T5 mulfunc
线性筛筛积性函数,只需要推出每个函数在\(n=p^k\)时的式子就都可以做。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+15 , n = 1e6;
bool vis[N];
int f[N],pri[N],cnt;
int calc(int x,int y){
while(x%y==0) x/=y;
return x;
}
int func(int x,int y){
int ret = 0;
while(x%y==0) x/=y,++ret;
return ret;
}
int qpow(int a,int b){
int ret = 1;
while(b){
if(b&1) ret = ret*a;
b>>=1,a=a*a;
}
return ret;
}
void init(int op){
f[1]=1;
for(int i = 2 ; i<=n ; i++){
if(!vis[i]){
pri[++cnt]=i;
if(op==1) f[i]=2;
if(op==2) f[i]=i+1;
if(op==3) f[i]=-1;
if(op==4) f[i]=i-1;
if(op==5) f[i]=0;
if(op==6) f[i]=i;
}
for(int j = 1 ; j<=cnt&&i*pri[j]<=n ; j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
if(op==1) f[i*pri[j]]=f[i]+f[calc(i*pri[j],pri[j])];
if(op==2) f[i*pri[j]]=f[i]+qpow(pri[j],func(i*pri[j],pri[j]))*f[calc(i*pri[j],pri[j])];
if(op==3) f[i*pri[j]]=0;
if(op==4) f[i*pri[j]]=f[i]*pri[j];
if(op==5) f[i*pri[j]]=0;
if(op==6) f[i*pri[j]]=f[i]*pri[j];
break;
}
f[i*pri[j]]=f[i]*f[pri[j]];
}
}
return ;
}
signed main(){
freopen("mulfuc.in","r",stdin);
freopen("mulfuc.out","w",stdout);
int op;scanf("%lld",&op);
init(op);
for(int i = 1 ; i<=n; i++) printf("%lld ",f[i]);
return 0;
}

浙公网安备 33010602011771号