校内题目{限制}
不仅仅局限与题目本身。
扩展gcd
可以解决
ax+by=c
的问题,
转换为
d=gcd(a,b)
,
a∗x/d+b∗y/d=c/d
的问题。
因为EX_GCD可以解决形如
a∗x+b∗y=gcd(a,b)
的问题。
原理如下:
gcd(a,b)=gcd(b,amodb)
朴素欧几里得证明如下:
令c=gcd(a,b)
−>a=mc,b=nc
,其中m,n为互质的正整数
令r=amodb−>r=a−qb,q∈N∗
,
−>r=a−qb=m∗c−qn∗c=(m−qn)∗c
b=n∗c,r=(m−qn)∗c
,且
n,(m−qn)
互质(假设
n,m−qn
不互质,则
n=xd,m−qn=yd
其中
x,y,d
都是正整数,且
d>1
,则
a=mc=(qx+y)∗d∗c,b=x∗d∗c,
这时
a,b
的最大公约数变成
dc
,与前提矛盾,所以
n,m−qn
一定互质.
−>gcd(b,amodb)=c=gcd(a,b)
所以可得:
一直递归求解到 b==0 时即可.
各种形如 ax≡b(modn)−>ax+ny=b 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int T,opt;
ll gcd(ll a,ll b){
return b==0 ? a : gcd(b,a%b);
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){x=1,y=0;return ;}
ll x0,y0;exgcd(b,a%b,x0,y0);x=y0;y=x0-(a/b)*y0;
}
int main(){
freopen("pay.in","r",stdin);
freopen("pay.out","w",stdout);
scanf("%d%d",&T,&opt);
while(T--){
ll a,b,c;
scanf("%I64d%I64d%I64d",&a,&b,&c);
ll x,y,x0,y0;
ll d=gcd(a,b);
if(c%d){
if(opt==1)printf("0\n");
else printf("0 0\n");
continue ;
}
a/=d,b/=d,c/=d;exgcd(a,b,x,y);
x*=c;x=(x%b+b)%b;//求解x>=0的一组解
y=(c-a*x)/b;y0=(y%a+a)%a;//得到x最小时y的最大值与y的最小值。
ll cnt=(y-y0)/a+1;//等差计算解的个数。
ll tot=(x+x+b*(cnt-1))*cnt/2+(y+y0)*cnt/2;//等差数列。。。
printf("%I64d",cnt);
if(opt==2)printf(" %I64d\n",tot);
else printf("\n");
}
return 0;
}
组合数
各种p为质数时
(O(1)||O(logn))
乱搞,不讲求解法了。
讲讲lucas?昨天谈过了,扩展lucas的细节。
那讲什么?组合数->不仅仅与求法。
依据数据范围求解是必备的素质,然而组合数最重要的是各种打表找规律!!!
找规律!!!
找规律!!!
不管是杨辉三角还是卡特兰数……打表看经验yy,(在NOIP范围)一定有很好的思路。
code就发一下,但重要的是如何发现规律的。
不要吝啬if的特判,稳才有高分!
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
template<class T>inline void read(T &res){
static char ch;T flag=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}
const long long p=1e9+7;
long long pow_mod(long long a,long long b) {
long long ans=1;a%=p;
while(b){
if(b&1)ans=ans*a%p;
b>>=1;a=a*a%p;
}
return ans;
}
long long fac[1000100];
void init(){
fac[0]=1;
for(long long i=1;i<=1000004;++i)
fac[i]=fac[i-1]*i%p;
}
long long C(long long n,long long m){
if(m>n)return 0LL;
if(m < 0 || n < 0) return 0LL;
return fac[n]*pow_mod(fac[m],p-2)%p*pow_mod(fac[n-m],p-2)%p;
}
long long t,n,m,opt;
int main(){
freopen("sumcomb.in","r",stdin);
freopen("sumcomb.out","w",stdout);
read(t);init();
for(register int i=1;i<=t;i++){
read(opt),read(n),read(m);
if(opt==2){
if(n+1==m){
printf("%I64d\n",0LL);
}else{
n++;
long long ans=C(n,m);
ans=(ans%p+p)%p;
printf("%I64d\n",ans);
}
}else{
if(n+1==m){
printf("%I64d\n",0LL);
}else{
m=n-m;n++;
long long ans=C(n,m);
ans=(ans%p+p)%p;
printf("%I64d\n",ans);
}
}
}
return 0;
}
/*
2
1 3 2
2 3 2
*/
容斥原理
说实话,本人觉得容斥原理是本人数论的短板(就像莫比乌斯反演一样),并不是其本身的难度,是其运用的灵活性让我不敢轻易在极高复杂度下使用(本人数学类复杂计算一塌糊涂,所以容斥的剪枝让我十分苦恼。),所以就贴个板子过了吧。
n个数中选择k个使得& | 的运算达到r。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const long long mod=1e9+7;
int n,K,r,T;
const int N=1e5+10,Mod=1e9+7,P=20;
int aa[N],cnt[1<<P],fac[N],vfac[N];
int mpow(int a,int b){
register int rt;
for(rt=1;b;b>>=1,a=(1LL*a*a)%Mod)
if(b&1)rt=(1LL*rt*a)%Mod;
return rt;
}
void init(int n){
fac[0]=1;
for(register int i=1;i<=n;i++)
fac[i]=1LL*fac[i-1]*i%Mod;
vfac[n]=mpow(fac[n],Mod-2);
for(register int i=n-1;i>=0;i--)
vfac[i]=1LL*vfac[i+1]*(i+1)%Mod;
}
int comb(int n, int m){
if(m>n)return 0;
return 1LL*fac[n]*vfac[m]%Mod*vfac[n-m]%Mod;
}
void fix(int &a){
if(a>=Mod)a-=Mod;
if(a<0)a+=Mod;
}
void sumup(){
for(register int i=0;i<P;i++){
register int s=(((1<<P)-1)^(1<<i));
for(register int ss=s;ss>0;ss=((ss-1)&s)){
cnt[ss]+=cnt[ss|(1<<i)];
fix(cnt[ss]);
}
cnt[0]+=cnt[1<<i];
fix(cnt[0]);
}
}
void sumdown(){
for(register int i=0;i<P;i++){
register int s=(((1<<P)- 1)^(1<<i));
for(register int ss=s;ss>0;ss=((ss-1)&s)){
cnt[ss]-=cnt[ss|(1<<i)];
fix(cnt[ss]);
}
cnt[0]-=cnt[1<<i];
fix(cnt[0]);
}
}
int main(){
freopen("kor.in","r",stdin);
freopen("kor.out","w",stdout);
scanf("%d",&T);init(1e5);
while(T--){
scanf("%d%d%d",&n,&K,&r);
memset(cnt,0,sizeof(cnt));
r=((~r)&((1<<20)-1));//&的话就不取反。
for(register int i=1;i<=n;i++)
scanf("%d",aa+i),aa[i]=((~aa[i])&((1<<20)-1)),cnt[aa[i]]++;
sumup();
for(register int s=0;s<(1<<P);s++)cnt[s]=comb(cnt[s],K);
sumdown();
printf("%d\n",cnt[r]);
}
return 0;
}