一些数学题

题单

P1082 [NOIP2012 提高组] 同余方程

整理一下式子,变为 \(ax+by=1\),exgcd 解之即可。

最小正整数解就 \((x\bmod b + b)\bmod b\) 即可。

时间复杂度 \(O(\log x)\),可以通过此题。

可以发现这题实质上就是求 \(x\) 的逆元,所以也可以直接欧拉定理计算(\(b\) 不一定是质数),复杂度 \(O(\sqrt{x})\)

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
#define y1 y233
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
   	int x=0;bool sgn=0;char ch=gt();
   	while(!__(ch)&&ch!=EOF){sgn|=(ch=='-');ch=gt();}
   	while(__(ch)){x=(x<<1)+(x<<3)+(ch-48);ch=gt();}
	return sgn?-x:x;
}
template<class T>
inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);
}
template<class T>
inline void printsp(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(32);
}
template<class T>
inline void println(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
    do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);pt(10);
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int a,b;
struct pii{
	int x,y;
	pii(int _x=0,int _y=0):x(_x),y(_y){}
};
pii exgcd(int a,int b){
	if(!b)return {1,0};
	pii tmp=exgcd(b,a%b);
	return {tmp.y,tmp.x-a/b*tmp.y}; 
}
signed main(){
	a=read(),b=read();
	println((exgcd(a,b).x%b+b)%b); 
	return 0;
}

P4549 【模板】裴蜀定理

首先,算出来的值只可能是 \(\gcd(a_1,a_2,...,a_n)\) 的倍数,证明直接提一下即可。

由于要求最小的,所以直接求 \(n\) 个数的 \(\gcd\) 即可。负数就转换成绝对值即可。

时间复杂度 \(O(n+\log \max a_i)\),如果 \(\gcd\) 初始值设成 \(\min a_i\) 则是 \(O(n+\log \min a_i)\) 的。

具体复杂度分析见这里

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top)pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,a[25],ans;
int gcd(int x,int y){
	return y?gcd(y,x%y):x; 
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		a[i]=abs(read());
		ans=gcd(ans,a[i]);
	}
	println(ans);
	return 0;
}

P2421 [NOI2002] 荒岛野人

注意:此题的答案不满足单调性,不能二分。

注意到答案的范围很小,考虑枚举。

假设我们当前枚举的答案为 \(k\),那么我们对于每对 \((i,j),i\not=j\),都要使以下方程无解:

\[C_i+p_ix\equiv C_j+p_jx\pmod k \]

注意这里的无解指的是原方程无解或者最小的 \(x>\max(L_i,L_j)\),也就是走不了 \(x\) 年。

实际上到这里就和这道题很像了。考虑转化:

\[C_i+p_ix+zk=C_j+p_jx \]

\[(p_i-p_j)x+zk=C_j-C_i \]

然后就可以 exgcd 了。负数的话取绝对值特殊处理一下即可。

\((C_j-C_i)\bmod \gcd(p_i-p_j,k)\not=0\) 时原方程无解,求最小解就对 \(\frac{C_j-C_i}{\gcd(p_i-p_j,k)}\) 取模即可。

对每一对 \((i,j)\) 都判断一遍,时间复杂度 \(O(Mn^2\log M)\),可以通过。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
const int N=20;
const int M=1e6;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,c[N],p[N],l[N],max_c;
struct pii{
	int x,y,d;
	pii(int _x=0,int _y=0,int _d=0):x(_x),y(_y),d(_d){}
};
pii exgcd(int a,int b){
	if(!b)return {1,0,a};
	pii tmp=exgcd(b,a%b);
	return {tmp.y,tmp.x-a/b*tmp.y,tmp.d}; 
}
inline bool check(int k){
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			int sgn=1;
			int a=p[i]-p[j],b=k;
			if(a<0) a=-a,sgn=-1;
			pii result=exgcd(a,b);
			if((c[j]-c[i])%result.d!=0) continue;
			int x=result.x*sgn*(c[j]-c[i])/result.d;
			int mod=k/result.d;
			x=(x%mod+mod)%mod;
			if(x<=min(l[i],l[j])) return 0;
		}
	}
	return 1;
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i) c[i]=read(),p[i]=read(),l[i]=read(),max_c=max(max_c,c[i]);
	for(int i=max_c;i<=M;++i){
		if(check(i)){
			println(i);
			return 0;
		}
	}
	return 0;
}

P2613 【模板】有理数取余

\(m=19260817\)

根据费马小定理,有 \(c\equiv ab^{m-2}\pmod m\)

根据模的性质,有 \(c\equiv (a\bmod m)(b\bmod m)^{m-2}\pmod m\)

然后边读入边取模即可。

时间复杂度 \(O(\log a)\),可以通过。

代码:

#include<bits/stdc++.h>
//#include<bits/extc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
#define y1 y233
#define rep(i,a,b,k) for(int (i)=(a),(_)=(b);(i)<=(_);(i)+=(k))
#define per(i,a,b,k) for(int (i)=(a),(_)=(b);(i)>=(_);(i)-=(k))
#define erp(i,h,u,e) for(int (i)=h[(u)];(i);(i)=e[(i)].nxt)
typedef long long ll;
//typedef __int128 lll;
typedef unsigned long long ull;
const int MOD=19260817;
using namespace std;
//using namespace __gnu_pbds;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline ll read(){
   	ll x=0;bool sgn=0;char ch=gt();
   	while(!__(ch)&&ch!=EOF){sgn|=(ch=='-');ch=gt();}
   	while(__(ch)){x=((x<<1)%MOD+(x<<3)%MOD+(ch-'0')%MOD)%MOD;ch=gt();}
	return sgn?-x:x;
}
inline void print(int x){
	static char st[70];short top=0;
	if(x<0)pt('-'),x=-x;
    do{st[++top]=(x%10+'0'),x/=10;}while(x);
    while(top)pt(st[top--]);
}
inline void printsp(int x){
	static char st[70];short top=0;
	if(x<0)pt('-'),x=-x;
    do{st[++top]=(x%10+'0'),x/=10;}while(x);
    while(top)pt(st[top--]);pt(32);
}
inline void println(ll x){
	static char st[70];short top=0;
	if(x<0)pt('-'),x=-x;
    do{st[++top]=(x%10+'0'),x/=10;}while(x);
    while(top)pt(st[top--]);pt(10);
}
inline void put_str(string s){
	int siz=s.size();
	rep(i,0,siz-1,1) pt(s[i]);
	printf("\n");
}
ll a,b,x,y;
inline void exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){x=1,y=0;return;}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
signed main(){
    a=read(),b=read(); 
   	if(!b){
   		printf("Angry!\n");
   		return 0;
	}
    exgcd(b,MOD,x,y);
    x=(x%MOD+MOD)%MOD;
    println(a*x%MOD);
	return 0;
}

CF757B - Bash's Big Day

很明显,只要选出来的 \(s\) 当中有任意一个约数 \(>1\),那么它们的 \(\gcd\)\(>1\)

考虑枚举这个约数 \(g\),对每个数都测试一下能不能整除,时间复杂度 \(O(n\max s_i)\),不能通过。

考虑在 \(O(\max s_i\sqrt{\max s_i})\) 时间内找出所有 \(s\) 的因数,并用桶记录出现次数,最后对于 \([2,\max s_i]\) 中的所有数取出现次数的 \(\max\) 即可。

时间复杂度 \(O(\max s_i\sqrt{\max s_i})\),可以通过。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
const int N=1e5+5;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,a[N],cnt[N],mx;
inline void fenjie(int x){
	for(int i=1;i*i<=x;++i){
		if(x%i==0){
			cnt[i]++;
			if(i*i!=x) cnt[x/i]++;
		}
	} 
}
signed main(){
	n=read();
	for(int i=1;i<=n;++i) a[i]=read(),mx=max(mx,a[i]);
	for(int i=1;i<=n;++i) fenjie(a[i]);
	int ans=1;
	for(int i=2;i<=mx;++i) ans=max(ans,cnt[i]);
	println(ans);
	return 0;
}

CF776B - Sherlock and his girlfriend

很明显,只需要质数分一组,合数分一组即可。

注意当 \(n<3\) 时全是质数,答案为 \(1\);否则为 \(2\)

线性筛筛一下质数即可。

时间复杂度 \(O(n)\),可以通过此题。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
const int N=1e5+5;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,prime[N],cnt;
bool not_prime[N];
inline void sieve(int n){
	not_prime[1]=1;
	for(int i=2;i<=n;++i){
		if(!not_prime[i]) prime[++cnt]=i;
		for(int j=1;j<=cnt;++j){
			if(1ll*prime[j]*i>n) break;
			not_prime[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		}
	}
}
signed main(){
	n=read();
	sieve(n+1);
	println(n<3?1:2);
	for(int i=2;i<=n+1;++i) printsp(not_prime[i]+1);
	return 0;
}

P2155 [SDOI2008] 沙拉公主的困惑

好题啊好题。

首先由于 \(n\ge m\),所以必有 \(n!\bmod m!=0\)

考虑证明一个东西:\(\gcd(a,b)=\gcd(a+k \times b,b)\)

\(\gcd(a,b)=g\),则 \(a=xg,b=yg\),且 \(\gcd(x,y)=1\)

推一下式子:

\[\gcd(a+k\times b,b) \]

\[=\gcd(xg+k\times yg,yg) \]

\[=\gcd(g(x+ky),yg) \]

\[=g\times \gcd(x+ky,y) \]

\(\gcd(x+ky,y)=m\),则有 \(m|x+ky\)\(m|y\),所以 \(m|x\)。由于 \(\gcd(x,y)=1\),所以 \(m=1\)

所以原式为:

\[g\times1=g \]

得证。

这有什么用呢?对于 \(\forall k\in[1,\frac{n!}{m!}],\forall x\in[1,m!]\),有

\[\gcd(x,m!)=\gcd(x+k\times m!,m!) \]

这样我们就将 \([1,n!]\) 中的数分为了 \(\frac{n!}{m!}\) 段长为 \(m!\) 的块,而每一个块的第 \(i\) 个数和 \(m!\)\(\gcd\) 都是相同的。

这样我们就只需要用一段的答案 \(\times\frac{n!}{m!}\) 即可。

考虑计算出第一段 \([1,m!]\) 中的答案,显然答案为 \(\phi(m!)\)。所以,最终的答案为:

\[\phi(m!)\times \frac{n!}{m!} \]

右边的可以直接递推。

然后 \(\phi(m!)\) 怎么求?

\(f_i=\phi(i!)\),考虑递推求出这个东西:

  • \(i\) 为质数

此时展开得

\[f_i=i!\prod_{p|i!}(1-\frac{1}{p}) \]

\(p\) 为质数。

继续拆:

\[=i\times(1-\frac{1}{i})\times(i-1)!\prod_{p|i!,p\not=i}(1-\frac{1}{p}) \]

这个时候发现原式实际上就是

\[=i\times(1-\frac{1}{i})\times(i-1)!\prod_{p|(i-1)!}(1-\frac{1}{p}) \]

所以:

\[f_i=(i-1)\times f_{i-1} \]

  • \(i\) 为合数

\[f_i=i!\times\prod_{p|i!}(1-\frac{1}{p}) \]

由于 \(p\) 并不包括 \(i\),所以:

\[f_i=i\times(i-1)!\times\prod_{p|(i-1)!}(1-\frac{1}{p}) \]

所以:

\[f_i=i\times f_{i-1} \]

筛一下质数即可。特别的,\(f_1=1\)

然后就解决了。

但此时还有一个问题:可能出现 \(R|n!\),而有可能 \(n!\) 中的 \(R\) 因子比 \(m!\) 中的少,这样算的时候就会出现 \(0\),就不对了。

解决方法也很简单:在计算时先扣去所有的 \(R\) 因子即可。

最后注意一下,如果 \(\lfloor\frac{n}{R}\rfloor>\lfloor\frac{m}{R}\rfloor\),那么 \(n!\)\(R\) 因子更多,此时答案才为 \(0\)

可以事先预处理,然后 \(O(1)\) 回答询问。时间复杂度 \(O(n+T)\),可以通过此题。

代码:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
const int N=1e7+5;
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int T,mod,n,m,prime[N],cnt;
bool not_prime[N];
inline void sieve(int n){
	not_prime[1]=1;
	for(int i=2;i<=n;++i){
		if(!not_prime[i]) prime[++cnt]=i;
		for(int j=1;j<=cnt;++j){
			if(1ll*i*prime[j]>n) break;
			not_prime[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		}
	}
}
ll fac[N],ifac[N],f[N];
inline ll ksm(ll a,ll b){
	ll res=1;a%=mod;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod,b>>=1;
	}
	return res;
}
inline void init(int n){
	sieve(n);
	fac[0]=1;
	for(int i=1;i<=n;++i){
		int x=i;
		while(x%mod==0) x/=mod;
		fac[i]=fac[i-1]*x%mod;
	}
	ifac[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=0;--i){
		int x=i+1;
		while(x%mod==0) x/=mod;
		ifac[i]=ifac[i+1]*x%mod;
	}
	f[1]=1;
	for(int i=2;i<=n;++i){
		int x=i-(!not_prime[i]);
		while(x%mod==0) x/=mod;
		f[i]=f[i-1]*x%mod;
	}
}
signed main(){
    T=read(),mod=read();
    init(N-5);
    while(T--){
    	n=read(),m=read();
	    if(n/mod>m/mod) println(0);
	    else println(f[m]*fac[n]%mod*ifac[m]%mod);
	}
	return 0;
}

P2152 [SDOI2009] SuperGCD

使用 Python 即可。

代码:

from fractions import *
a = int(input())
b = int(input())
print(gcd(a,b))

正经人谁写高精。

CF632D - Longest Subsequence

首先很明显有 \(a\le\text{lcm(a,b)}\),所以可以直接剔除 \(>m\)\(a_i\)

考虑用每个 \(a_i\) 的出现次数去更新它的倍数的答案。复杂度调和级数,可以通过。

代码:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
const int N=1e6+5; 
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,m,a[N],b[N],tot,cnt[N],ans[N];
signed main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
    	a[i]=read();
    	if(a[i]<=m){
    		b[++tot]=a[i];
    		cnt[a[i]]++;
		}
	}
	sort(b+1,b+tot+1);
	int new_tot=unique(b+1,b+tot+1)-(b+1);
	tot=new_tot;
	for(int i=1;i<=tot;++i) for(int j=b[i];j<=m;j+=b[i]) ans[j]+=cnt[b[i]];
	int lcm=0,len=-1;
	for(int i=1;i<=m;++i) if(ans[i]>len) len=ans[i],lcm=i;
	printf("%d %d\n",lcm,len);
	tot=0;
	for(int i=1;i<=n;++i) if(lcm%a[i]==0) printsp(i);
	return 0;
}

然后是几个问题:

  • 如果当前答案 \(x\) 不是 \(\text{lcm}\),怎么办?

注意到我们是从小往大枚举,且只当 \(ans_i>\text{当前答案}\) 时才更新,所以 \(x\) 必为 \(\text{lcm}\)

  • 为什么 \(len\) 的初始值为 \(-1\)

首先只有 \(\min a_i>m\) 的时候长度才是 \(0\),而此时 \(tot=0\),所以不会输出;而对于其他情况,\(ans_i=0\) 是不合法的,此时它不是任何数的 \(\text{lcm}\),所以不能以 \(0\) 开始。

时间复杂度 \(O(n\log n)\)

CF582A -GCD Table

CF687B - Remainders Game

P2261 [CQOI2007]余数求和

首先取模不太好搞,考虑转换:

\[\sum_{i=1}^{n}k\bmod i \]

\[=\sum_{i=1}^{n}k-\lfloor\dfrac{k}{i}\rfloor\times i \]

\(k\) 拆出去:

\[=nk-\sum_{i=1}^{n}\lfloor\dfrac{k}{i}\rfloor\times i \]

然后整除分块即可。

具体地,令当前块为 \([l,r]\),那么该块对 \(\sum\) 的贡献就是:

\[\sum_{i=l}^{r}\lfloor\dfrac{k}{l}\rfloor\times i \]

\[=\lfloor\dfrac{k}{l}\rfloor\times\sum_{i=l}^{r}i \]

使用等差数列求和公式:

\[=\lfloor\dfrac{k}{l}\rfloor\times\dfrac{(l+r)(r-l+1)}{2} \]

然后就做完了。

小细节:计算 \(r\) 时,如果 \(\lfloor\dfrac{k}{l}\rfloor=0\),说明后面全是 \(0\),直接令 \(r=n\) 即可;否则,令 \(r=\min(n,\lfloor\dfrac{k}{\lfloor\dfrac{k}{l}\rfloor}\rfloor)\)

时间复杂度 \(O(\sqrt{n})\),可以通过此题。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline int read(){
	int x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
int n,k;
signed main(){
	n=read(),k=read();
	ll ans=1ll*n*k,sum=0;
	for(int l=1,r;l<=n;l=r+1){
		if(k/l) r=min(n,k/(k/l));
		else r=n;
		sum+=1ll*(k/l)*(l+r)*(r-l+1)/2;
	}
	println(ans-sum);
	return 0;
}

P2158 [SDOI2008] 仪仗队

P6583 回首过去

P4139 上帝与集合的正确用法

P2567 [SCOI2010]幸运数字

P5505 [JSOI2011]分特产

P3223 [HNOI2012] 排队

P2822 [NOIP2016 提高组] 组合数问题

首先考虑 \(O(n^2)\) 递推求组合数,然后对 \(k\) 取模,最后只需要查询有多少个为 \(0\) 的组合数即可。

直接暴力查询显然是会寄的,由于这里是求的是 \((n,m)\)\((0,0)\)(坐标) 满足条件的组合数数量,因此直接上二维前缀和即可。

实现过程中我们可以直接将杨辉三角填充为一个 \(2000\times2000\) 的矩形,然后对于 \(j>i\) 的位置,直接令 \(sum_{i,j}=sum_{i,i}\) 即可,因为后面的组合数都是不合法的。

最后每次询问的答案就是 \(sum_{n,m}\)

时间复杂度 \(O(n^2)\)

代码:

#include<bits/stdc++.h>
const int N=2e3+5;
using namespace std;
int T,mod,C[N][N],ans[N][N];
signed main(){
	scanf("%d%d",&T,&mod);
	C[0][0]=1;
	for(int i=1;i<=2000;++i){
		C[i][0]=1;
		for(int j=1;j<=i;++j){
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
			ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+(!C[i][j]);
		}
		for(int j=i+1;j<=2000;++j) ans[i][j]=ans[i][i];
	}
	while(T--){
		int n,m;
		scanf("%d%d",&n,&m);
		printf("%d\n",ans[n][m]); 
	}
	return 0;
}

P3214 [HNOI2011] 卡农

P2532 [AHOI2012]树屋阶梯

P3200 [HNOI2009]有趣的数列

CF1278F - Cards

P3228 [HNOI2013]数列

CF961G - Partitions

UVA11181 - 条件概率 Probability|Given

P1365 WJMZBMR打osu! / Easy

P4316 绿豆蛙的归宿

P1297 [国家集训队]单选错位

P2111 考场奇遇

P1850 [NOIP2016 提高组] 换教室

P2520 [HAOI2011]向量

P1349 广义斐波那契数列

P1939 【模板】矩阵加速(数列)

P2447 [SDOI2010] 外星千足虫

P7112 【模板】行列式求值

P3216 [HNOI2011] 数学作业

P4910 帕秋莉的手环

考虑 DP。

\(f_{i,0/1}\) 表示第 \(i\) 个珠子是木/金的方案数。

显然有递推式 \(f_{i,0}=f_{i-1.1},f_{i,1}=f_{i-1,0}+f_{i-1,1}\),但是 \(f_{1,0/1}\) 不知道。

先不考虑第一个珠子,可以写出矩阵:

\[\begin{bmatrix} f_{i,0} & f_{i,1} \end{bmatrix} = \begin{bmatrix} f_{i-1,0} & f_{i-1,1} \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix} \]

所以

\[\begin{bmatrix} f_{n,0} & f_{n,1} \end{bmatrix} = \begin{bmatrix} f_{1,0} & f_{1,1} \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}^{n-1} \]

先求出 \(f_{1,0/1}\),然后矩阵快速幂即可。

强制让第一个珠子为金色,那么有 \(f_{1,0}=0,f_{1,1}=1\)。此时第 \(n\) 个珠子可金可木,答案为 \(f_{n,0}+f_{n,1}\)

强制让第一个珠子为木色,那么有 \(f_{1,0}=1,f_{1,1}=0\)。此时第 \(n\) 个珠子只能是金,答案为 \(f_{n,1}\)

还要特判一下 \(n=1\) 的情况,此时答案为 \(1\)

最后加起来即可。

时间复杂度 \(O(T2^3\log n)\)(不严谨),可以通过此题。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize("Ofast")
#define gt getchar
#define pt putchar
//typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
//typedef __uint128_t ulll;
const int mod=1e9+7;
using namespace std;
inline bool __(char ch){return ch>=48&&ch<=57;}
inline ll read(){
	ll x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	return sgn?-x:x;
}
template<class T> inline void print(T x){
	static char st[70];short top=0;
	if(x<0)pt('-');
 	do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
inline void put_str(string s){
	int siz=s.size();
	for(int i=0;i<siz;++i) pt(s[i]);
	printf("\n");
}
struct Matrix{
	int n,m;
	ll val[2][2];
	Matrix(int _n=0,int _m=0){
		n=_n,m=_m;
		memset(val,0,sizeof(val));
	}
};
inline Matrix operator*(const Matrix &a,const Matrix &b){
	Matrix c(a.n,b.m);
	for(int i=0;i<c.n;++i){
		for(int k=0;k<a.m;++k){
			for(int j=0;j<c.m;++j){
				c.val[i][j]=(c.val[i][j]+a.val[i][k]*b.val[k][j]%mod)%mod;	
			}
		}
	}
	return c;
}
inline Matrix ksm(Matrix a,ll b){
	Matrix c=a;
	b--;
	while(b){
		if(b&1) c=c*a;
		a=a*a,b>>=1;
	}
	return c;
}
int T;
ll n;
inline void solve(){
	n=read();
	if(n==1){
		println(1);
		return;
	}
	Matrix tmp(1,2),base(2,2);
	tmp.val[0][0]=0,tmp.val[0][1]=1;
	base.val[0][0]=0,base.val[0][1]=1;
	base.val[1][0]=1,base.val[1][1]=1;
	Matrix result=tmp*ksm(base,n-1);
	ll ans=(result.val[0][0]+result.val[0][1])%mod;
	swap(tmp.val[0][0],tmp.val[0][1]);
	result=tmp*ksm(base,n-1);
	ans=(ans+result.val[0][1])%mod;
	println(ans);
}
signed main(){
	T=read();
	while(T--) solve();
	return 0;
}

P2151 [SDOI2009] HH去散步

P4035 [JSOI2008]球形空间产生器

P2973 [USACO10HOL]Driving Out the Piggies G

posted @ 2024-02-28 15:17  Southern_Dynasty  阅读(13)  评论(0)    收藏  举报