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\)矩阵满足下式

\[\begin{bmatrix} A_{0,0} & A_{0,1} & ... & A_{0,m-1}\\ A_{1,0} & ... & ... & A_{1,m-1}\\ ... & ... & ... & ...\\ A_{m-1,0} & ... & ... & A_{m-1,m-1} \end{bmatrix} \begin{pmatrix} f_{i,0}\\ f_{i,1}\\ ...\\ f_{i,m-1} \end{pmatrix} = \begin{pmatrix} f_{i+1,0}\\ f_{i+1,1}\\ ...\\ f_{i+1,m-1} \end{pmatrix}\]

\(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;
}
posted @ 2026-08-27 16:52  lrj3247  阅读(2)  评论(0)    收藏  举报