一些数学题
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\),都要使以下方程无解:
注意这里的无解指的是原方程无解或者最小的 \(x>\max(L_i,L_j)\),也就是走不了 \(x\) 年。
实际上到这里就和这道题很像了。考虑转化:
然后就可以 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(x+ky,y)=m\),则有 \(m|x+ky\) 且 \(m|y\),所以 \(m|x\)。由于 \(\gcd(x,y)=1\),所以 \(m=1\)。
所以原式为:
得证。
这有什么用呢?对于 \(\forall k\in[1,\frac{n!}{m!}],\forall x\in[1,m!]\),有
这样我们就将 \([1,n!]\) 中的数分为了 \(\frac{n!}{m!}\) 段长为 \(m!\) 的块,而每一个块的第 \(i\) 个数和 \(m!\) 的 \(\gcd\) 都是相同的。
这样我们就只需要用一段的答案 \(\times\frac{n!}{m!}\) 即可。
考虑计算出第一段 \([1,m!]\) 中的答案,显然答案为 \(\phi(m!)\)。所以,最终的答案为:
右边的可以直接递推。
然后 \(\phi(m!)\) 怎么求?
记 \(f_i=\phi(i!)\),考虑递推求出这个东西:
- \(i\) 为质数
此时展开得
\(p\) 为质数。
继续拆:
这个时候发现原式实际上就是
所以:
- \(i\) 为合数
由于 \(p\) 并不包括 \(i\),所以:
所以:
筛一下质数即可。特别的,\(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]余数求和
首先取模不太好搞,考虑转换:
把 \(k\) 拆出去:
然后整除分块即可。
具体地,令当前块为 \([l,r]\),那么该块对 \(\sum\) 的贡献就是:
使用等差数列求和公式:
然后就做完了。
小细节:计算 \(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}\) 不知道。
先不考虑第一个珠子,可以写出矩阵:
所以
先求出 \(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;
}