一些数学题

题单

P1082 [NOIP2012 提高组] 同余方程

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

最小正整数解就 (xmodb+b)modb 即可。

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

可以发现这题实质上就是求 x 的逆元,所以也可以直接欧拉定理计算(b 不一定是质数),复杂度 O(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(a1,a2,...,an) 的倍数,证明直接提一下即可。

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

时间复杂度 O(n+logmaxai),如果 gcd 初始值设成 minai 则是 O(n+logminai) 的。

具体复杂度分析见这里

代码:

#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),ij,都要使以下方程无解:

Ci+pixCj+pjx(modk)

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

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

Ci+pix+zk=Cj+pjx

(pipj)x+zk=CjCi

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

(CjCi)modgcd(pipj,k)0 时原方程无解,求最小解就对 CjCigcd(pipj,k) 取模即可。

对每一对 (i,j) 都判断一遍,时间复杂度 O(Mn2logM),可以通过。

代码:

#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

根据费马小定理,有 cabm2(modm)

根据模的性质,有 c(amodm)(bmodm)m2(modm)

然后边读入边取模即可。

时间复杂度 O(loga),可以通过。

代码:

#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(nmaxsi),不能通过。

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

时间复杂度 O(maxsimaxsi),可以通过。

代码:

#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] 沙拉公主的困惑

好题啊好题。

首先由于 nm,所以必有 n!modm!=0

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

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

推一下式子:

gcd(a+k×b,b)

=gcd(xg+k×yg,yg)

=gcd(g(x+ky),yg)

=g×gcd(x+ky,y)

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

所以原式为:

g×1=g

得证。

这有什么用呢?对于 k[1,n!m!],x[1,m!],有

gcd(x,m!)=gcd(x+k×m!,m!)

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

这样我们就只需要用一段的答案 ×n!m! 即可。

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

ϕ(m!)×n!m!

右边的可以直接递推。

然后 ϕ(m!) 怎么求?

fi=ϕ(i!),考虑递推求出这个东西:

  • i 为质数

此时展开得

fi=i!p|i!(11p)

p 为质数。

继续拆:

=i×(11i)×(i1)!p|i!,pi(11p)

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

=i×(11i)×(i1)!p|(i1)!(11p)

所以:

fi=(i1)×fi1

  • i 为合数

fi=i!×p|i!(11p)

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

fi=i×(i1)!×p|(i1)!(11p)

所以:

fi=i×fi1

筛一下质数即可。特别的,f1=1

然后就解决了。

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

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

最后注意一下,如果 nR>mR,那么 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

首先很明显有 alcm(a,b),所以可以直接剔除 >mai

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

代码:

#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 不是 lcm,怎么办?

注意到我们是从小往大枚举,且只当 ansi>当前答案 时才更新,所以 x 必为 lcm

  • 为什么 len 的初始值为 1

首先只有 minai>m 的时候长度才是 0,而此时 tot=0,所以不会输出;而对于其他情况,ansi=0 是不合法的,此时它不是任何数的 lcm,所以不能以 0 开始。

时间复杂度 O(nlogn)

CF582A -GCD Table

CF687B - Remainders Game

P2261 [CQOI2007]余数求和

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

i=1nkmodi

=i=1nkki×i

k 拆出去:

=nki=1nki×i

然后整除分块即可。

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

i=lrkl×i

=kl×i=lri

使用等差数列求和公式:

=kl×(l+r)(rl+1)2

然后就做完了。

小细节:计算 r 时,如果 kl=0,说明后面全是 0,直接令 r=n 即可;否则,令 r=min(n,kkl)

时间复杂度 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;
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(n2) 递推求组合数,然后对 k 取模,最后只需要查询有多少个为 0 的组合数即可。

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

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

最后每次询问的答案就是 sumn,m

时间复杂度 O(n2)

代码:

#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。

fi,0/1 表示第 i 个珠子是木/金的方案数。

显然有递推式 fi,0=fi1.1,fi,1=fi1,0+fi1,1,但是 f1,0/1 不知道。

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

[fi,0fi,1]=[fi1,0fi1,1]×[0111]

所以

[fn,0fn,1]=[f1,0f1,1]×[0111]n1

先求出 f1,0/1,然后矩阵快速幂即可。

强制让第一个珠子为金色,那么有 f1,0=0,f1,1=1。此时第 n 个珠子可金可木,答案为 fn,0+fn,1

强制让第一个珠子为木色,那么有 f1,0=1,f1,1=0。此时第 n 个珠子只能是金,答案为 fn,1

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

最后加起来即可。

时间复杂度 O(T23logn)(不严谨),可以通过此题。

代码:

#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 @   Southern_Dynasty  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示