各種友(e)善(xin)數論總集(未完待續),從入門到絕望

快速冪

題目描述
【題意】
求a^b mod c,a,b,c都是整數。
【輸入格式】
一行三個整數 a、b、c。 0 ≤ a,b,c ≤ 10^9
【輸出格式】
一行,a^b mod c的值。
【樣例輸入】
2 5 7
【樣例輸出】
4

題目鏈接就不放出來了,因爲這個OJ已經很卡了,且不登錄不能看題目。

其實,我們容易知道,冪次有個性質:當b%2==0b\%2==0ab=(aa)b/2a^{b}=(a*a)^{b/2}

用腦子想想就知道。

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
LL  kms(LL  a,LL  b,LL  c)//a^b%c
{
	LL  ans=1%c;/*防止c==1*/a%=c;
	while(b)//b!=0
	{
		if(b&1)ans=(ans*a)%c;
		a=(a*a)%c;b>>=1;//b/=2;
	}
	return  ans;
}
int  main()
{
	LL  a,b,c;scanf("%lld%lld%lld",&a,&b,&c);
	printf("%lld\n",kms(a,b,c));
	return  0;
}

擴展歐幾里得

GCD

如果我們要求兩個數字的最大公約數怎麼求?

如果兩個數字a,ba,b存在最大公約數kk,那麼把aa寫成xkxk,把bb寫成ykyk

那麼我們知道b%a=(y%x)kb\%a=(y\%x)k,但是yy又與xx互質,所以僅當x=1x=1時,yy爲最大公約數,此時a=ka=k也就是最大公約數,而且我們也知道y%xy\%x也是與x互質的。

所以我們可以設新的aa&#x27;與新的bb&#x27;a=b%aa&#x27;=b\%a,b=ab&#x27;=a,當a=0a&#x27;=0時,bb就是最大公約數。

而且一開始a&gt;ba&gt;b也不怕,做了一次交換後就換過來了(b%a=bb\%a=b)

int  gcd(int  x,int  y)
{
	if(x==0)return  y;
	return  gcd(y%x,x);
}

擴展歐幾里得

如何求ax+by=cax+by=c的一個整數解?

盡看擴展歐幾里得。

首先,我們設ax+by=gcd(a,b)ax+by=gcd(a,b)

那我們就想了,如果有個方程爲0x+gcd(a,b)y=gcd(a,b)0x+gcd(a,b)y=gcd(a,b),不就很妙了嗎?
y=1,xy=1,x隨便等於什麼都不影響最終結果,只不過一般取0,怕爆long long。

然後我們再證一個:

當我們解出了(b%a)x+ay=gcd(a,b)(b\%a)x+ay=gcd(a,b),怎麼推到ax+by=gcd(a,b)ax+by=gcd(a,b)

我們可以推導一下(x⌊x⌋爲向下取整):
ax+by=(bb/aa)x+ayax+by=(b-⌊b/a⌋*a)x&#x27;+ay&#x27;
ax+by=ab/ax+ay+bxax+by=-a*⌊b/a⌋x&#x27;+ay&#x27;+bx&#x27;
ax+by=a(yb/ax)+bxax+by=a(y&#x27;-⌊b/a⌋x&#x27;)+bx&#x27;

b/a⌊b/a⌋就是C++的b/ab/a

所以,我們發現b%ab\%aaa就是gcdgcd過程,所以最後也會得到00gcd(a,b)gcd(a,b)

然後再看:如果gcd(a,b)cgcd(a,b)|c的話,設d=c/gcd(a,b)d=c/gcd(a,b),然後把x,yx,y直接乘以dd就沒什麼毛病了。

但是如果不整除就無解,我們又可以證一證:


首先gcd(a,b)cgcd(a,b)|c是肯定有解的,我們可以知道,而且ax+by=cax+by=c可以類似於axcmod&ThinSpace;&ThinSpace;bax≡c\mod b,我們可以根據同餘的性質得出我們可以把a,b,ca,b,c的同一個因數gcd(a,b)gcd(a,b)提出,得到a,b,ca&#x27;,b&#x27;,c&#x27;,化成axcmod&ThinSpace;&ThinSpace;ba&#x27;x≡c&#x27;\mod b&#x27;,而且a’與b’不互質,那麼我們可以得出,同餘方程axcmod&ThinSpace;&ThinSpace;bax≡c\mod ba,ba,b互質時有解,或者當cc整除gcd(a,b)gcd(a,b)時有解。(好像說偏了

然後,如果gcd(a,b)gcd(a,b)與c不整除,我們再設z=gcd(a,b),c=kz+dz=gcd(a,b),c=kz+d

那麼我們把原式化爲:azx+bzy=kz+da&#x27;zx+b&#x27;zy=kz+d同除zz可得

ax+by=k+dza&#x27;x+b&#x27;y=k+ \frac{d}{z}

但是d&lt;zd&lt;z所以dz\frac{d}{z}是個分數,但是ax+bya&#x27;x+b&#x27;y都是整數,所以無解


至此,結束了(感覺又過了一個世紀)!

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
LL  exgcd(LL  a,LL  b,LL  &x,LL  &y/*引用*/)
{
	if(a==0)
	{
		x=0;y=1;//0x+gcd(a,b)y=gcd(a,b) 
		return  b;
	}
	else
	{
		LL  tx,ty;
		LL  d=exgcd(b%a,a,tx,ty);
		x=ty-(b/a)*tx;//公式 
		y=tx;//公式 
		return  d;
	}
}
int  main()
{
	LL  a,b,c,x,y;scanf("%lld%lld%lld",&a,&b,&c);
	LL  d=exgcd(a,b,x,y);
	if(c%d!=0)printf("no solution!\n");
	else
	{
		c/=d;
		printf("%lld %lld\n",x*c,y*c);
	}
	return  0;
}

其實也沒什麼難度。

同餘系列

同餘方程

題目描述

【題意】
已知a,b,m,求x的最小正整數解,使得ax≡b(mod m)
【輸入格式】
一行三個整數 a,b,m。 1 ≤ a,b,m ≤ 10^9 
【輸出格式】
一行一個整數x,無解輸出"no solution!"
【樣例輸入】
2 5 7
【樣例輸出】
6

我們會發現axbax≡b(modmod mm)其實就是axmy=bax-my=b也就是ax+my=bax+my=bmm的正負無多大必要,反正只是求xx,而且m&gt;0m&gt;0方便的地方在於gcdgcd爲正數)

然後我們運用擴展歐幾里德求出xx,但是如何求出最小的xx

學過不定式的人都知道,當axax[a,b][a,b]時,mymy也可以相應的加[a,b][a,b]時([a,b][a,b]就是a,ba,b的最小公倍數),x,yx,y能得出另外一組整數解,也就是x[a,b]/a,y+[a,b]/bx-[a,b]/a,y+[a,b]/b,又因爲[a,b]=ab/(a,b)[a,b]=a*b/(a,b),所以就是xb/(a,b),ya/(a,b)x-b/(a,b),y-a/(a,b)(a,b)(a,b)gcd(a,b)gcd(a,b))。

而且[a,b][a,b]是能同時被a,ba,b整除的最小的數字。

所以,我們可以用d=gcd(a,b),x=(x%(b/d)+b/d)%(b/d)d=gcd(a,b),x=(x\%(b/d)+b/d)\%(b/d)得出xx最小的正整數解

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
LL  exgcd(LL  a,LL  b,LL  &x,LL  &y)
{
	if(a==0)
	{
		x=0;y=1;
		return  b;
	}
	else
	{
		LL  tx,ty;
		LL  d=exgcd(b%a,a,tx,ty);
		x=ty-(b/a)*tx;
		y=tx;
		return  d;
	}
}
LL  zbs(LL  x){return  x<0?-x:x;}
int  main()
{
	LL  a,b,c,x,y;scanf("%lld%lld%lld",&a,&c,&b);
	LL  d=exgcd(a,b,x,y);//求擴展歐幾里得 
	if(c%d!=0)printf("no solution!\n");
	else
	{
		c/=d;
		x*=c;
		LL  k=b/d;
		x=((x%k)+k)%k;//最小的正整數解 
		printf("%lld\n",x);
	}
	return  0;
}

同餘方程組

題目描述

【題意】 
同餘方程是這樣的:已知a,b,n,求x的最小正整數解,使得ax=b(mod m)
同餘方程組是這樣:也是求x的最小正整數解,但已知b數組和m數組的情況下,
x=b[1](mod m[1]),
x=b[2](mod m[2]),
x=b[3](mod m[3]),
~~~~~~
x=b[n](mod m[n])
【輸入格式】
一行一個整數 n(1<=n<=?)
下來n行每行兩個整數b[i],m[i]。 1 ≤b[i],m[i] ≤ 10^9 
【輸出格式】
一行一個整數x,無解輸出"no solution!"
【樣例輸入】
3
3 5
2 3
2 7

【樣例輸出】
23

這道題就十分的友(e)善(xin)了

現在我們可以列出x=b1+m1y1,x=b2+m2y2...x=b_{1}+m_{1}y_{1},x=b_{2}+m_{2}y_{2}...

我們拿上面減下面的出:m1y1m2y2=b2b1m_{1}y_{1}-m_{2}y_{2}=b_{2}-b_{1}也就是ax+by=cax+by=c的形式。

那麼我們寫成ax+by=c,a=m1,b=m2ax&#x27;+by&#x27;=c,a=m_{1},b=m_{2}(這裏是m2-m_{2}也無所謂,但是是正數後面算最小正整數比較方便。),c=b2b1,x=y1,y=y2,c=b_{2}-b_{1},x&#x27;=y_{1},y&#x27;=y_{2}

然後解出xx&#x27;,將xx&#x27;帶入x=b1+axx=b_{1}+ax&#x27;得出了xx的其中一個解,而且我們又知道xx要變只能加上或減去b/(a,b)b/(a,b),所以xx就只能加減(b/(a,b)a=[a,b])(b/(a,b)*a=[a,b]),所以我們又可以列出一個新的方程:xb1+axx≡b_{1}+ax&#x27;(modmod [a,b][a,b])(其中的b1+axb_{1}+ax&#x27;表示的是x的其中一組解),爲了後面的方便,我們把這個方程代替爲第二個方程,後面就是個二三做一次,三四做一次…

然後我們就可以拿這個方程與第三個方程做這種事,重複如此,最後的b?b_{?}就是答案,但是,別忘了是求最小整數解。


求最小整數解有兩種方法:

我常用的方法:

開局直接把每個bib_{i}(不包括求出來的b?b_{?})模aia_{i},並且把每次求出的xx都做一次最小整數解,那麼得出來的新的方程的b?b_{?}一定小於新的m?m_{?},爲什麼,m?m_{?}[a,b][a,b],而b?b{?}bi+axb_{i}+ax得出的。(這裏的aa表示mim_{i}bb表示mi+1m_{i+1}),我們知道xb/(a,b)1x≤b/(a,b)-1,那麼ax[a,b]aax≤[a,b]-abi&lt;ab_{i}&lt;a,那麼ax+bi&lt;[a,b]ax+b_{i}&lt;[a,b],所以b?b_{?}小於m?m_{?},也就是不用模了。

#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
LL  exgcd(LL  a,LL  b,LL  &x,LL  &y)
{
	if(a==0)
	{
		x=0;y=1;
		return  b;
	}
	else
	{
		LL  tx,ty;
		LL  d=exgcd(b%a,a,tx,ty);
		x=ty-(b/a)*tx;
		y=tx;
		return  d;
	}
}
LL  a1,b1;
bool  solve(LL  a2,LL  b2)
{
	LL  a=a1,b=a2,c=b2-b1,x,y;
	LL  d=exgcd(a,b,x,y);
	if(c%d!=0)return  false;
	else
	{
		c/=d;
		x*=c;
		LL  lca=b/d;
		x=((x%lca)+lca)%lca;//最小正整數解 
		b1=a1*x+b1;a1=a*b/d;//不用模 
		return  true;
	}
}
int  main()
{
	int  n;scanf("%d",&n);
	scanf("%lld%lld",&b1,&a1);b1%=a1;//開局模一模 
	for(int  i=2;i<=n;i++)
	{
		LL  a,b;scanf("%lld%lld",&b,&a);b%=a;//開局模一模 
		if(!solve(a,b))
		{
			printf("no solution!\n");
			return  0;
		}
	}
	printf("%lld\n",b1/*不用模*/);
	return  0;
}

但是後面,我又發現了一個不是很麻煩的方法,就是結尾模一下就行了。。。

//少了去正過程,中間會有許多數字變成負數
//而且更容易爆long long
#include<cstdio>
#include<cstring>
using  namespace  std;
typedef  long  long  LL;
LL  exgcd(LL  a,LL  b,LL  &x,LL  &y)
{
	if(a==0)
	{
		x=0;y=1;
		return  b;
	}
	else
	{
		LL  tx,ty;
		LL  d=exgcd(b%a,a,tx,ty);
		x=ty-(b/a)*tx;
		y=tx;
		return  d;
	}
}
LL  zbs(LL  x){return  x<0?-x:x;}
LL  a1,b1;
bool  solve(LL  a2,LL  b2)
{
	LL  a=a1,b=a2,c=b2-b1,x,y;
	LL  d=exgcd(a,b,x,y);
	if(c%d!=0)return  false;
	else
	{
		c/=d;
		x*=c;//可能爲負數 
		b1=a1*x+b1;a1=a*b/d;
		return  true;
	}
}
int  main()
{
	int  n;scanf("%d",&n);
	scanf("%lld%lld",&b1,&a1);
	for(int  i=2;i<=n;i++)
	{
		LL  a,b;scanf("%lld%lld",&b,&a);
		if(!solve(a,b))
		{
			printf("no solution!\n");
			return  0;
		}
	}
	LL  zhl=a1<0?-a1:a1;//絕對值 
	printf("%lld\n",(b1%zhl+zhl)%zhl/*尾巴模一模*/);
	return  0;
}

以上便是作者的心得。

一點想法

當然,如果xx前面也有係數怎麼辦?我們求出的值可能不能整除於xx的係數?

在我們求完時,我們得到的最後一個方程就是結果。(這裏爲方便直接把bnb_{n}bbmnm_{n}mm

axbax≡b(modmod mm)

aa什麼時候是整數?

是不是當my+b0my+b≡0(modmod aa)的時候?(yy爲任意整數)

那麼我們解出這個方程的yy的最小整數解就行了。

當然,這只是本蒟蒻的一點點假設,並沒有實踐過。

高次同餘方程

BSGS

【題意】
求解 A^x=B(mod C)  (0<=x<C),求x的值。
已知:C、A、B都是正整數,2 <= C < 2^31, 2 <= A < C, 1 <= B < C。
(A與C互質)
【輸入格式】
多組測試數據。
每組數據一行三個整數:C,A,B。
【輸出格式】
每組數據一行,如果有解輸出x的最小正整數解,否則輸出"no solution!"。
【樣例輸入】
5 2 1
5 2 2
5 2 3
5 2 4
5 3 1
5 3 2
5 3 3
5 3 4
5 4 1
5 4 2
5 4 3
5 4 4
12345701 2 1111111
1111111121 65537 1111111111
【樣例輸出】
0
1
3
2
0
3
1
2
0
no solution!
no solution!
1
9584351
462803587

BSGSBSGS,又名北上廣深。

你是不是覺得這個東西十分的高大上?

其實還挺簡單的QAQ。

我們知道AxBmod&ThinSpace;&ThinSpace;CA^{x}≡B\mod C

我們設mceil(c)m≡ceil(\sqrt c)ceilceil向上取整,在C++的cmath庫內)

爲什麼是ceilceil,學完你就知道了。

那麼xx可以寫成mabma-b的形式

也就是AmabBmod&ThinSpace;&ThinSpace;CA^{ma-b}≡B\mod C

推導一波:

AmabBmod&ThinSpace;&ThinSpace;CA^{ma-b}≡B\mod C

AmaAbBmod&ThinSpace;&ThinSpace;CA^{ma}*A^{-b}≡B\mod C

AmaBAbmod&ThinSpace;&ThinSpace;CA^{ma}≡B*A^{b}\mod C

出現了點倪端了,不知道大家有沒有學過歐拉定理?

aφ(p)1mod&ThinSpace;&ThinSpace;pa^{φ(p)}≡1\mod p(當aapp互質的,φ(p)φ(p)爲歐拉函數,後面會講)

所以xx最大爲φ(p)φ(p),爲了方便,我們不在φ(p)φ(p)來找,而是在p(p&gt;φ(p))p(p&gt;φ(p))裏面找,因爲是找最小的,所以在兩個範圍找都沒事,但pp更方便。

當然,這裏你得會Hash表(map也可以,或者像我們機房的一個大佬,不會Hash表,但會SBT)。

然後,我們枚舉bb00mm,並把BAbmod&ThinSpace;&ThinSpace;CB*A^{b}\mod C放進Hash表,然後在枚舉aa11mm,在Hash表裏面找到最大的bb(爲了求最小的xx)滿足AmaBAbmod&ThinSpace;&ThinSpace;CA^{ma}≡B*A^{b}\mod C,那麼就可以返回答案了。

而且aa11開始,可以讓mabma≥b,不會出現負數,並且可以很好的枚舉從00CC的所有數字。

#include<cstdio>
#include<cstring>
#include<cmath>
#define  N  2800000
using  namespace  std;
typedef  long  long  LL;
class  hash
{
	public:
		struct  node
		{
			LL  c;int  x,next;
		}a[N];int  last[N],len,h;
		hash(){h=2750159;/*用線性篩篩出的素數*/}
		void  ins(int  x,LL  c){LL  y=c%h;len++;a[len].x=x;a[len].c=c;a[len].next=last[y];last[y]=len;}
		int  find(LL  c)
		{
			LL  x=c%h;
			for(int  k=last[x];k;k=a[k].next)
			{
				if(a[k].c==c)return  a[k].x;//求最大的b 
			}
			return  -1;
		}
		void  clear(){memset(last,0,sizeof(last));len=0;}
}zjj;
LL  kms(LL  a,LL  b,LL  c)//快速冪 
{
	LL  ans=1%c;a%=c;
	while(b)
	{
		if(b&1)ans=(ans*a)%c;
		a=(a*a)%c;b>>=1;
	}
	return  ans;
}
LL  bsgs(LL  a,LL  b,LL  c)
{
	zjj.clear();
	LL  d=ceil(sqrt(c));
	LL  now=b;
	for(int  i=0;i<=d;i++)
	{
		zjj.ins(i,now);
		now=(now*a)%c;
	}//枚舉b 
	LL  lim=now=kms(a,d,c);
	for(int  i=1;i<=d;i++)
	{
		int  x;
		if((x=zjj.find(now))!=-1)return  i*d-x;
		now=(now*lim)%c;
	}//枚舉a 
	return  -1;
}
int  main()
{
	LL  a,b,c;
	while(scanf("%lld%lld%lld",&c,&a,&b)!=EOF)
	{
		int  ans=bsgs(a,b,c);
		if(ans==-1)printf("no solution!\n");
		else  printf("%d\n",ans);
	}
	return  0;
}

exBSGS

我們會發現上面的過程有一個限制,就是A,CA,C必須互質,不是,這就太苛刻了吧,毒瘤出題人會有歪念頭的!

於是,我們想到了同餘的性質:abmod&ThinSpace;&ThinSpace;ca≡b\mod c,當a,b,ca,b,c有同一個公因子dd時,可以把dd消掉,得出adbdmod&ThinSpace;&ThinSpace;cd\frac{a}{d}≡\frac{b}{d}\mod \frac{c}{d},那麼,我們也可以把A,CA,C的最大公因數提出呀,但是BB也必須要整除於這個最大公因數,否則就是無解的,很容易想,我們設A,CA,C的最大公因數爲DD,然後原式就等於ADAx1BDmod&ThinSpace;&ThinSpace;CD\frac{A}{D}A^{x-1}≡\frac{B}{D}\mod \frac{C}{D}如果AABD\frac{B}{D}還是不互質,我們就繼續,我們設AA的係數爲KK,那麼KK就在此過程中不斷增加,且xx減的也越來越多。

當然,此過程存在一個噁心的特判,就是當B=KB=K時,直接返回現在提了幾次最大公因數,也就是xx減了多少。

那麼,最後,我們只需要計算Aximod&ThinSpace;&ThinSpace;CA^{x-i}\mod C&#x27;的時候再乘KK就行了,然後算出xix-i後再加上ii就行了。

一道不錯的噁心的例題

這也是我在luoguluogu對的第一道黑題(雖然插頭DP也很多,但沒交

#include<cstdio>
#include<cstring>
#include<cmath>
#define  N  210000
using  namespace  std;
typedef  long  long  LL;
class  Hash//class多好用啊
{
	public:
		struct  node
		{
			int  x,next;LL  c;
		}a[N];int  len,last[N];LL  h;
		Hash(){h=200043;}
		void  ins(int  x,LL  c){LL  y=c%h;a[++len].x=x;a[len].c=c;a[len].next=last[y];last[y]=len;}
		void  clear(){len=0;memset(last,0,sizeof(last));}
		int  find(LL  c)
		{
			LL  x=c%h;
			for(int  k=last[x];k;k=a[k].next)
			{
				if(a[k].c==c)return  a[k].x;
			}
			return  -1;
		}
}zjj;
LL  ksm(LL  a,LL  b,LL  c)
{
	LL  ans=1%c;a%=c;
	while(b)
	{
		if(b&1)ans=(ans*a)%c;
		a=(a*a)%c;b>>=1;
	}
	return  ans;
}
LL  gcd(LL  a,LL  b){return  (!a?b:gcd(b%a,a));}
LL  solve(LL  a,LL  c,LL  d)
{
	a%=d;c%=d;//這一步也不是很需要,但是可以更遠離爆long long
	LL  b=1,cnt=0,tt;
	while((tt=gcd(a,d))!=1)
	{
		if(c==b)return  cnt;//特判
		if(c%tt)return  -1;//無解
		d/=tt;c/=tt;b=(b*a/tt)%d;a%=d;cnt++;
	}
	LL  lim=ceil(sqrt(d*1.0)),now=c;
	for(int  i=0;i<=lim;i++)
	{
		zjj.ins(i,now);
		now=(now*a)%d;
	}
	now=((tt=ksm(a,lim,d))*b)%d/*重複利用*/;
	for(int  i=1;i<=lim;i++)
	{
		int  y=zjj.find(now);
		if(y!=-1)return  i*lim-y+cnt;
		now=(now*tt)%d;
	}
	return  -1;
}
int  main()
{
	LL  a,c,d;
	while(scanf("%lld%lld%lld",&a,&d,&c)!=EOF)
	{
		if(a==0  &&  d==0  &&  c==0)break;
		zjj.clear();
		LL  ans=solve(a,c,d);
		if(ans!=-1)printf("%lld\n",ans);
		else  printf("No Solution\n");
	}
	return  0;
}

線性篩素數

題目描述
【題意】
素數表如下:
2、3、5、7、11、13、17…………
有n次詢問,每次問第幾個素數是誰?
【輸入格式】
第一行n(1<=n<=10000)
下來n行,每行一個整數pi,表示求第pi個素數。1<=pi<=100 0000
【輸出格式】
每次詢問輸出對應的素數。
【樣例輸入】
3
1
4
7
【樣例輸出】
2
7
17

線性篩素數有兩種,埃式篩和歐拉篩。

埃式篩

我們可以知道,一個數字如果不是質數,那麼它的最小質因子一定小於等於這個數字的開平方。

很容易想到,不詳細解釋。

那麼求11bb的素數的話,我們可以知道,11bb的合數的最小質因數也是一定小於等於b\sqrt b的,那麼我們只需要求出b\sqrt b內的所有素數並且暴力for一遍,空間小,複雜度爲O(nn)O(n\sqrt n)

for(int  i=2;i<=sqrt(b);i++)
{
    if(mp[i]==false)//爲素數
    {
        for(int  j=i;j<=b;j+=i)mp[j]=true;
    }
}

給出一種神奇卡常版:

for(register  int  i=3;i<=sqrt(m);i+=2)
{
    if(a[i/2]==true)
    {
        for(register  int  j=i*3;j<=m;j+=i*2)a[j/2]=false;
    }
}

歐拉篩

後面題目主要用歐拉篩,複雜度O(nlognlogn)O(nlognlogn)

這個就很神奇了,先給出代碼:

#include<cstdio>
#include<cstring>
#define  N  21000000
using  namespace  std;
int  ins[2100000];//素數表
bool  mp[N];
int  main()
{
	for(int  i=2;i<=20000000;i++)
	{
		if(!mp[i])ins[++ins[0]]=i;//存入素數表
		for(int  j=1;j<=ins[0]  &&  ins[j]*i<=20000000;j++)
		{
			mp[ins[j]*i]=true;
			if(i%ins[j]==0)break;
		}
	}
	int  n;scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		printf("%d\n",ins[x]);
	}
	return  0;
}

許多人疑問這一句

if(i%ins[j]==0)break;

如果insjiins_{j}|i,那麼就可以break了,Why?那麼我們設iidinsjd*ins_{j},設k&gt;jk&gt;jiinsk=dinsjinsk=(dinsk)insji*ins_{k}=d*ins_{j}*ins_{k}=(d*ins_{k})*ins_{j},我們可以知道dinsk&gt;dinsjd*ins_{k}&gt;d*ins_{j},那麼在以後的循環,iinski*ins_{k}會被重複標記,那麼此時就可以break了,一句話就這麼精妙。

超級卡常版:

for(register  int  i=3;i<=m;i+=2)
{
    if(a[i/2]==true)b[++k]=i;
    for(register  int  j=1;j<=k  &&  b[j]*i<=m;++j)
    {
        a[b[j]*i/2]=false;
        if(i%b[j]==0)break;
    }
}

當然,慎用卡常版。

歐拉函數

講解

phi(x)phi(x)就是表示在11~xxxx互質的數量,叫歐拉函數。

又記作φ(x)φ(x)(貌似在前面看到過呢!)

那麼我們馬上知道φ(x)=x1φ(x)=x-1(當xx爲質數時)

但是我們又知道幾個公式去求呢?

首先,我們做一道題,設ab=ya*b=y,那麼在11yy,與a,ba,b互質的數字有多少?(a,ba,b爲質數,且aba≠b

注意:因爲a,ba,b爲質數,且aba≠b,那麼(a,b)=1,[a,b]=ab(a,b)=1,[a,b]=a*b

那麼我們知道1y1-y中整除於aa的數量爲ya\frac{y}{a},整除於bb的數量爲yb\frac{y}{b},整除於abab的數量爲yab\frac{y}{ab},那麼1y1-y中整除於aa或者整除於bb的數量爲ya+ybyab\frac{y}{a}+\frac{y}{b}-\frac{y}{ab}

那麼與a,ba,b互質就是yyayb+yab=y(11a1b+1ab)=y(11a)(11b)y-\frac{y}{a}-\frac{y}{b}+\frac{y}{ab}=y(1-\frac{1}{a}-\frac{1}{b}+\frac{1}{ab})=y(1-\frac{1}{a})(1-\frac{1}{b}),那麼如果是求a2b=ya^{2}*b=y呢?

我們會發現,我們求出了aba*b以內與a,ba,b互質的數爲c1,c2,c3...c_{1},c_{2},c_{3}...,而[a,b][a,b]也就是abab是整除於aabb的,那麼ci%(ab)0,(ab)%(ab)==0,(ci+ab)%ab0c_{i}\%(ab)≠0,(ab)\%(ab)==0,(c_{i}+ab)\%ab≠0,也就是說a2ba^{2}*b就是aa個循環的aba*b,也就是說phi(a2b)=y(11a)(11b)phi(a^{2}*b)=y(1-\frac{1}{a})(1-\frac{1}{b})。(注意:如果是abca*b*c的話仍然按第一個問題來看,第二個問題是針對素數有次方的情況)

那麼我們就得出了phi(x)=x(11a1)(11a2)...=(a1b1a2b2a3b3...anbn)a11a1a21a2...=(a1b11(a11))(a2b21(a21))...phi(x)=x*(1-\frac{1}{a1})*(1-\frac{1}{a2})...=(a1^{b1}a2^{b2}a3^{b3}...an^{bn})*\frac{a1-1}{a1}*\frac{a2-1}{a2}...=(a1^{b1-1}(a1-1))(a2^{b2-1}(a2-1))...=φ(a1b1)φ(a1b1)...=φ(a1^{b1})φ(a1^{b1})...(其中的mm表示xx可分解成幾個不同的質因數,a1,a2,...ana1,a2,...an就是分解出來的不同的質因數,bibi表示的是aiai的次方)

那麼我們又可以得出一個公式φ(ab)=φ(a)φ(b)φ(ab)=φ(a)φ(b)a,ba,b互質),其實這關額證明也很簡單,因爲他們互質,所以他們所含的質因子不同,那麼不會對對方產生任何影響。

當然,不僅僅止步於此,我們設一個質數xx,如何求φ(ax)φ(ax)

a%x0a\%x≠0時,a,xa,x互質,所以φ(ax)=φ(a)φ(x)φ(ax)=φ(a)φ(x),當a%x=0a\%x=0是,xxaa的其中一個質因數,根據上面可得xx可以合併到aa去得到φ(ax)=φ(a)xφ(ax)=φ(a)x

然後我們發現此過程可以套到歐拉篩裏面。

#include<cstdio>
#include<cstring>
#define  N  21000000
using  namespace  std;
int  ins[2100000]/*素數表*/,phi[N]/*歐拉函數*/;
int  main()
{
	for(int  i=2;i<=20000000;i++)
	{
		if(!phi[i])ins[++ins[0]]=i,phi[i]=i-1;//不用mp,直接用歐拉函數的數組判斷
		for(int  j=1;j<=ins[0]  &&  ins[j]*i<=20000000;j++)
		{
			if(i%ins[j]==0)//phi(ax)=phi(a)x
			{
				phi[ins[j]*i]=ins[j]*phi[i];
				break;
			}
			else  phi[ins[j]*i]=phi[ins[j]]*phi[i];//phi(ax)=phi(a)phi(x)
		}
	}
	int  n;scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		printf("%d\n",phi[x]);
	}
	return  0;
}

兩道水題

法雷級數

題目描述
【題意】
法雷級數Fi的定義如下:
給定一個i(i>=2),那麼所有的 a/b (0<a<b<=i 且 gcd(a,b)==1 )組成了Fi,
例如: 
F2 = {1/2} 
F3 = {1/3, 1/2, 2/3} 
F4 = {1/4, 1/3, 1/2, 2/3, 3/4} 
F5 = {1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5} 
你的任務就是統計對於給出的i,Fi集合的元素個數。
【輸入格式】
第一行n(1<=n<=10000)
下來n行,每行一個整數x,表示求Fx的元素個數。(1<=x<=2000 0000)
【輸出格式】
每次詢問輸出一行一個整數,即Fx的元素個數。
【樣例輸入】
4
2
3
4
5
【樣例輸出】
1
3
5
9

我已經不想說什麼了,就是求22xx的所有phiphi函數的值。

#include<cstdio>
#include<cstring>
#define  N  21000000
using  namespace  std;
int  ins[2100000],phi[N];
long  long  sum[N];//要用long long
int  main()
{
	for(int  i=2;i<=20000000;i++)
	{
		if(!phi[i])ins[++ins[0]]=i,phi[i]=i-1;
		for(int  j=1;j<=ins[0]  &&  ins[j]*i<=20000000;j++)
		{
			if(i%ins[j]==0)
			{
				phi[ins[j]*i]=ins[j]*phi[i];
				break;
			}
			else  phi[ins[j]*i]=phi[ins[j]]*phi[i];
		}
		sum[i]=sum[i-1]+phi[i];//sum統計
	}
	int  n;scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		printf("%lld\n",sum[x]);
	}
	return  0;
}

可見點數

hehe

【題意】
給出一個正整數N,求在(x,y){0<=x,y<=N}的區域中,
從(0,0)點可以看到的點的個數,
不包括(0,0)自己。
比如上圖就是N=5的圖,0 ≤ x, y ≤ 5 

【輸入格式】
輸入有 C (1 ≤ C ≤ 1000) 組數據
每組數據給出一個正整數 N (1 ≤ N ≤ 1000)
【輸出格式】
每組數據輸出三個整數分別是:第幾組,N,和可以看到的點數
【樣例輸入】
4
2
4
5
231
【樣例輸出】
1 2 5
2 4 13
3 5 21
4 231 32549

也是水題一道,我們發現沿着對角線切割發現左邊的可見點數與右邊的可點點數不就是法雷級數嗎?

當然,有三個點需要我們特地加上:(1,1),(1,0),(0,1)(1,1),(1,0),(0,1)

那麼答案就是sumi2+3sum_{i}*2+3

#include<cstdio>
#include<cstring>
#define  N  1010
using  namespace  std;
int  ins[2100],phi[N];
long  long  sum[N];
int  main()
{
	for(int  i=2;i<=1000;i++)
	{
		if(!phi[i])ins[++ins[0]]=i,phi[i]=i-1;
		for(int  j=1;j<=ins[0]  &&  ins[j]*i<=1000;j++)
		{
			if(i%ins[j]==0)
			{
				phi[ins[j]*i]=ins[j]*phi[i];
				break;
			}
			else  phi[ins[j]*i]=phi[ins[j]]*phi[i];
		}
		sum[i]=sum[i-1]+phi[i];
	}
	int  n;scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		printf("%d %d %lld\n",i,x,sum[x]*2+3);
	}
	return  0;
}

原根


ax1mod&ThinSpace;&ThinSpace;ba^{x}≡1\mod b,滿足最小的正整數xx稱爲aabb的階,很明顯a,ba,b互質纔有階。


原根:當aabb的階等於φ(b)φ(b)時,稱aabb的原根。

我們這裏規定原根的數量表示的是11mm中爲mm原根的數量。

原根有以下性質:

  1. a,a2,a3,a4...,aφ(b)a,a^{2},a^{3},a^{4}...,a^{φ(b)}在模bb的情況下能不重複的形成bb的所有質因數。(兩個集合的數字都有φ(b)φ(b)個)
  2. akφ(b)1mod&ThinSpace;&ThinSpace;ba^{kφ(b)}≡1\mod b
  3. 一個數字mm有原根的必要條件爲2,4,pn,2pn2,4,p^{n},2p^{n},其中pp爲爲奇素數,nn爲正整數。
  4. 一個數字mm的原根數量φ(φ(m))φ(φ(m))

那麼如何證呢?

歐拉定理

好,讓我們來看看歐拉定理。(霧)

之前跟大家講了會填坑的

我們知道aφ(b)1mod&ThinSpace;&ThinSpace;ba^{φ(b)}≡1\mod ba,ba,b不互質),但是這個有什麼用?

這個爲階最大爲φ(b)φ(b)提供了一個必要的條件。

我們來證證吧。


我們設bb的質因子爲a1,a2,a3...,aφ(b)a_{1},a_{2},a_{3}...,a_{φ(b)}

現在有一個xxbb互質,設c1=xa1,c2=xa2...c_{1}=xa_{1},c_{2}=xa_{2}...

  1. 沒有一個cicjmod&ThinSpace;&ThinSpace;b(ij)c_{i}≡c_{j}\mod b(i≠j)

這個讓我們用反證法證一證吧:

cicjmod&ThinSpace;&ThinSpace;bc_{i}≡c_{j}\mod b

cicj0mod&ThinSpace;&ThinSpace;bc_{i}-c_{j}≡0\mod b

x(aiaj)0mod&ThinSpace;&ThinSpace;bx(a_{i}-a_{j})≡0\mod b

因爲xxbb互質,那麼aiaja_{i}-a_{j}整除於bb,但是aiajb|a_{i}-a_{j}|<b,所以不成立。

  1. cic_{i}除以bb得到的餘數uubb互質。

依然用反證法證一證:

ciumod&ThinSpace;&ThinSpace;bc_{i}≡u\mod b那麼cikb=uc_{i}-kb=ukk爲未知整數)。

bbkk不互質,那麼(b,k)&gt;1(b,k)&gt;1那麼我們可以知道原式可化爲:aix(b,k)+kb(a,b)=u(a,b)\frac{a_{i}x}{(b,k)}+\frac{kb}{(a,b)}=\frac{u}{(a,b)}

那麼後面兩個分數都是整數,那aix(b,k)\frac{a_{i}x}{(b,k)}就必須是整數,但是ai,xa_{i},x都與bb互質,所以aixa_{i}x就與(b,k)(b,k)互質,所以不成立。

依據上面我們可以知道cc集合在mod&ThinSpace;&ThinSpace;b\mod b的情況下可以全部不重複映射到aa集合,那麼我們將c1,c2,c3...c_{1},c_{2},c_{3}...乘起來得到xφ(b)a1a2a3a4...aφ(b)a1a2a3...aφ(b)mod&ThinSpace;&ThinSpace;bx^{φ(b)}a_{1}a_{2}a_{3}a_{4}...a_{φ(b)}≡a_{1}a_{2}a_{3}...a_{φ(b)}\mod b也就是(xφ(b)1)a1a2a3a4...aφ(b)0mod&ThinSpace;&ThinSpace;b(x^{φ(b)}-1)a_{1}a_{2}a_{3}a_{4}...a_{φ(b)}≡0\mod b,因爲a1...a_{1}...都與bb互質,所以xφ(b)10mod&ThinSpace;&ThinSpace;bx^{φ(b)}-1≡0\mod b,所以xφ(b)1mod&ThinSpace;&ThinSpace;bx^{φ(b)}≡1\mod b

證畢。

當然,費馬小定理就是歐拉定理的特殊情況,bb爲素數時,xb11mod&ThinSpace;&ThinSpace;bx^{b-1}≡1\mod b


原根部分性質證明(數量證不出來,一個還沒填的坑)

注:一下證明中指數都在11φ(b)φ(b)


如同歐拉定理,我們先證沒有一個aiajmod&ThinSpace;&ThinSpace;b(i&gt;j)a^{i}≡a^{j}\mod b(i&gt;j)

反證法:

假如有aiajmod&ThinSpace;&ThinSpace;b(i&gt;j)a^{i}≡a^{j}\mod b(i&gt;j)

那麼:

aiajmod&ThinSpace;&ThinSpace;ba^{i}≡a^{j}\mod b

aiaj0mod&ThinSpace;&ThinSpace;ba^{i}-a^{j}≡0\mod b

aj(aij1)0mod&ThinSpace;&ThinSpace;ba^{j}(a^{i-j}-1)≡0\mod b

那麼我們知道aabb互質,所以aja^{j}bb互質,而aabb的階爲φ(b)φ(b)ij&lt;φ(b)i-j&lt;φ(b),所以aij10mod&ThinSpace;&ThinSpace;ba^{i-j}-1≡0\mod b不成立,所以aiajmod&ThinSpace;&ThinSpace;b(i&gt;j)a^{i}≡a^{j}\mod b(i&gt;j)不成立。

再證aia^{i}除以bb得到的餘數uubb互質。

反證法:

aiumod&ThinSpace;&ThinSpace;ba^{i}≡u\mod b

aibk=ua^{i}-bk=u

然後就跟歐拉定理裏面的一樣了。

所以我們就證出了a,a2,a3,a4...,aφ(b)a,a^{2},a^{3},a^{4}...,a^{φ(b)}在模bb的情況下能不重複的形成bb的所有質因數。(兩個集合的數字都有φ(b)φ(b)個)


akφ(b)1mod&ThinSpace;&ThinSpace;ba^{kφ(b)}≡1\mod b

這個怎麼證?我們知道ad1mod&ThinSpace;&ThinSpace;ba^{d}≡1\mod bddaabb的階)

那麼akd%b(ad)k%b1j%b1a^{kd}\%b≡(a^{d})^{k}\%b≡1^{j}\%b≡1

由於原根的階就是φ(b)φ(b),所以得證。


當然,數量以及是否有原根我不會證。

聽說要羣論,我果然還是太菜了。

擴展:原根的求法

這個就很棒了,我們可以先假設pp是奇素數,然後求pp的原根。


以下內容摘自https://oi-wiki.org/math/primitive-root/,OIwiki也十分有名,大家可以上去搜一些OI的東西。

在這裏插入圖片描述

當然,我們還會做一些解釋,其中這是求pp是奇素數的,當然,可以吧求法中的p1p-1換成phi(x)phi(x),其他的也換一下也沒有多大問題,依然可以證,不過首先pp要有原根纔可以用。

圖片中類似Vi[1,m]Vi∈[1,m]:的東西表示循環。

這裏的裴蜀定理指的是如果有一個二元一次不定式滿足:ax+by=cax+by=cc(a,b)c|(a,b))那麼此不定式就有無數個整數解,當然,在上面同餘的時候我們也類似的證了證,那麼也就沒多大問題了,因爲是擴展,就沒有代碼了QAQ。


代碼

題目描述
給出n個正整數,求每個正整數的原根個數。

注意:不是每一個數都有原根!這道題和1159不一樣!

輸入
第一行一個數n

接下來n行,一行一個數xi。

輸出
一行一個數,第i行是xi的原根個數(無原根則輸出0)。

樣例輸入
8
1
2
3
4
8
9
10
18
樣例輸出
0
1
1
1
0
2
2
2
提示

1<n<=10000


1<=xi<=2*10^7
#include<cstdio>
#include<cstring>
#define  N  21000000
using  namespace  std;
int  ss[2100000],phi[N];
bool  mt[N];
int  main()
{
	mt[2]=mt[4]=true;phi[1]=1;
	for(int  i=2;i<=20000000;i++)
	{
		if(!phi[i])
		{
			phi[i]=i-1,ss[++ss[0]]=i;
			if(i!=2)
			{
				long  long  now=i;
				while(now<=N)//存在原根的地方
				{
					mt[now]=true;
					now*=i;
				}
			}
		}
		for(int  j=1;j<=ss[0]  &&  ss[j]*i<=20000000;j++)
		{
			if(i%ss[j])phi[ss[j]*i]=phi[ss[j]]*phi[i];
			else
			{
				phi[ss[j]*i]=phi[i]*ss[j];
				break;
			}
		}
	}
	int  n;scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		if(mt[x]==true  ||  (x%2==0  &&  mt[x/2]==true))printf("%d\n",phi[phi[x]]);//存在原根
		else  printf("0\n");
	}
	return  0;
}

高斯消元

題目描述
【題意】
有n個未知數x1,x2,x3……xn,滿足:
a11*x1+a12*x2+a13*x3……+a1n*xn=b1
a21*x1+a22*x2+a23*x3……+a2n*xn=b2
……………………………………
an1*x1+an2*x2+an3*x3……+ann*xn=bn
求x1,x2,x3……xn的值。(保證有解)
【輸入格式】
第一行給出n(1<=n<=100)
下來n行,每行給出n+1個實數,分別是 ai1~ain 和bi。
【輸出格式】
輸出x1,x2,x3……xn的值(相鄰兩個用一個空格隔開,每個數保留3位小數)
【樣例輸入】
3
2.5 5.0 3.0 32.5
1.0 4.5 2.0 22
4.0 3.5 1.5 26.5
【樣例輸出】
3.000 2.000 5.000

【提示】

aij>0

普通

我們小學做nn元一次方程都是消元的嗎,結果沒想到OI中叫高斯消元,好像很高大尚。

其實就是用11式消去22nn式的第一項,用22式消去33nn式的第二項…

O(n3)O(n^{3})

#include<cstdio>
#include<cstring>
#define  N  110
using  namespace  std;
double  a[N][N],f[N];
int  n;
void  guess()
{
	for(int  i=1;i<=n;i++)
	{
		for(int  j=i+1;j<=n;j++)
		{
			double  bi=a[j][i]/a[i][i];//求出比例
			for(int  k=n+1;k>=i;k--)
			{
				a[j][k]-=a[i][k]*bi;//暴力double減
			}
		}
	}
	for(int  i=n;i>=1;i--)
	{
		double  bi=a[i][n+1];
		for(int  j=i+1;j<=n;j++)bi-=a[i][j]*f[j];
		f[i]=bi/a[i][i];
	}
}
int  main()
{
	scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		for(int  j=0;j<=n;j++)scanf("%lf",&a[i][j+1]);
	}
	guess();
	for(int  i=1;i<n;i++)printf("%.3lf ",f[i]);
	printf("%.3lf\n",f[n]);
	return  0;
}

輾轉相除法

輾轉相除不是gcdgcd嗎?b%a,ab\%a,a呀。

但是在guessguess裏面,小數有時總是這麼不盡任意,雖然有四捨五入,但是整數在調試還是什麼方面都很強!

而且如果要求求的是整數解就很棒棒了,所以在矩陣樹中的消元許多人寧願多個loglog也要打輾轉相除。

複雜度O(n3logn)O(n^{3}logn)

因爲沒有題目驗證正確性,將在矩陣樹給出輾轉相除版。

先講講:

我們用第xx行消掉第y(y&gt;x)y(y&gt;x)行的第xx項時(根據高斯消元我們知道兩行的前x1x-1項都已經被消掉了),我們不是暴力直接除,而是將第xx項每一行乘以ay,x/ax,xa_{y,x}/a_{x,x}減到第yy行,然後交換x,yx,y行,我們會發現其實就是對xx項做類似輾轉相除的操作然後改變一下其他項,當然,爲了節省時間也可以不交換,不過得用一些其他方法實現類似交換的操作。

矩陣樹與證明

又是一個大的內容。。。

參考資料:

https://www.cnblogs.com/zj75211/p/8039443.html

https://blog.csdn.net/qq_34921856/article/details/79409754

https://www.cnblogs.com/twilight-sx/p/9064208.html

https://blog.csdn.net/a_crazy_czy/article/details/72971868

https://www.cnblogs.com/candy99/p/6420935.html

https://www.cnblogs.com/xzzzh/p/6718926.html

https://baike.baidu.com/item/Binet-Cauchy%E5%AE%9A%E7%90%86/8255247

https://baike.baidu.com/item/%E5%8D%95%E4%BD%8D%E7%9F%A9%E9%98%B5/8540268?fr=aladdin

你看看這麼多,就知道這個有多噁心了!!!!!!!!!!!!!!

未了結的坑

矩陣樹是解決一個圖中生成樹的數量。

目前我只會做無向圖的,而且也只會證無向圖的,當然,按我現在對矩陣樹的理解,有沒有重邊應該也無所謂,大家可以試一試,因爲時間緊迫,所以就不證明了,下面的默認沒有重邊。

因爲老師開始要求學幾何了,就只能拋坑了QAQ

未填完的坑:

  1. 有向圖的矩陣樹。
  2. 變元矩陣樹。
  3. 證明有重邊也無所謂

無向圖

關聯矩陣

在無向圖裏面,關聯矩陣是nmn*m的一個矩陣(nn是點數,mm的邊數),如果第kk條無向邊是(i&gt;j)(i-&gt;j),那麼矩陣裏的(i,k)(i,k)點值++++(j,k)(j,k)的點值--。(倒過來也可以,無所謂)

給張圖更好的理解:

在這裏插入圖片描述

我們設無向圖的關聯矩陣爲BBBB轉置矩陣爲BTB^{T}


轉置矩陣:

就是把現矩陣的第一行作爲第一列,第一列作爲第一行。

在這裏插入圖片描述

就是轉置矩陣。


矩陣乘法:

這裏放上度孃的解釋https://baike.baidu.com/item/%E7%9F%A9%E9%98%B5%E4%B9%98%E6%B3%95/5446029?fr=aladdin:

在這裏插入圖片描述


Kirchhoff矩陣

這裏只講無向圖的Kirchhoff矩陣。

Kirchhoff矩陣就是BBTBB^{T},也就是一個nnn*n的矩陣

BBTBB^{T}ij=k=1mBi,kBk,jT=k=1mBi,kBj,k_{ij}=\sum_{k=1}^{m}B_{i,k}B^{T}_{k,j}=\sum_{k=1}^{m}B_{i,k}B_{j,k}

所以我們可以知道,Kirchhoff矩陣的(i,j)(i,j)項就是BB的第ii行與第jj行的內積。

iji≠j時,如果有Bi,kBj,k=0B_{i,k}B_{j,k}=0,那麼說明第kk條邊不是(ij)(i-j)的。相反Bi,kBj,k=1B_{i,k}B_{j,k}=-1,就代表有一條邊,所以我們知道,Kirchhoff矩陣的(i,j)(i,j)項表示的是iji-j邊的個數的相反數,目前只能是1,0-1,0

i=ji=j時, 如果有Bi,kBi,k=0B_{i,k}B_{i,k}=0,那麼說明他不在第kk條邊上,相反Bi,kBi,k=1B_{i,k}B_{i,k}=1就是在,所以Kirchhoff矩陣的(i,i)(i,i)項表示的是ii的度數。

當然,還有個簡單的構建方法,就是(度數矩陣 - 鄰接矩陣)。

爲什麼要將關聯矩陣,證明的時候就知了。

我們容易知道這個矩陣的每個行和每個列的和爲00

因爲度數爲正,其他數爲負,這一行(列)的所有負數的和就是度數的相反數。

行列式

對於一格rrr*r的行列式AA

行列式det(A)det(A)的值爲:i=1r!(1)ka1,b1a2,b2a3,b3...ar,br\sum^{r!}_{i=1}(-1)^{k}a_{1,b_{1}}a_{2,b_{2}}a_{3,b_{3}}...a_{r,b_{r}},其中aija_{ij}AA矩陣的第ii行第jj列。

b1,b2,b3,b4...brb_{1},b_{2},b_{3},b_{4}...b_{r}是長度爲rr的任意一個全排列,然後全排列的個數有r!r!,然後kk表示的是(1,b1),(2,b2),(3,b3)...(1,b_{1}),(2,b_{2}),(3,b_{3})...的逆序對數((i1,j1),(i2,j2)(i1&lt;i2,j1&gt;j2)(i_{1},j_{1}),(i_{2},j_{2})(i_{1}&lt;i_{2},j_{1}&gt;j_{2})就是一對逆序對)。

其實就是選rr個點,行和列不重複

比如:

222*2的矩陣OO

{1234}\left\{ \begin{matrix} 1 &amp; 2\\ 3 &amp; 4\end{matrix} \right\}

det(O)=1423=2det(O)=1*4-2*3=-2

行列式有一些beautifulbeautiful的性質。


定理1AAAA的轉置矩陣的行列式相等。

我們發現轉置後點的相對位置不變,第ii行變成第ii列,第jj列變成了第jj行,本質沒變,所以行列式相等。

定理2:當某行(列)的值全部爲0,那麼這個矩陣的行列式爲00

怎麼選點都有個00,證畢。

定理3:某一行與某一行交換,行列式取負。

證明:

(1)ka1,b1a2,b2a3,b3...ar,br(-1)^{k}a_{1,b_{1}}a_{2,b_{2}}a_{3,b_{3}}...a_{r,b_{r}}中,現將i,j(i&lt;j)i,j(i&lt;j)行互換。

設從i+1i+1j1j-1,有q1q_{1}個數字的bb(也就是列數)小於bib_{i},有q2q_{2}個大於bib_{i},有p1p_{1}個數字小於bjb_{j},有p2p_{2}個大於bjb_{j}N=(j1)(i+1)+1=ji+1N=(j-1)-(i+1)+1=j-i+1,不可能有相等的。

那麼交換前bi,bjb_{i},b_{j}的貢獻有q1+p2q_{1}+p_{2}對,交換後有q2+p1q_{2}+p_{1}

那麼兩個相減爲:q1+p2q2p1=q1+p2(Nq1)(Np2)=2q1+2p22N=2(q1+p2N)q_{1}+p_{2}-q_{2}-p_{1}=q_{1}+p_{2}-(N-q_{1})-(N-p_{2})=2q_{1}+2p_{2}-2N=2(q_{1}+p_{2}-N),而卻bib_{i}bjb_{j}交換的又會加11或減11,所以取反。

交換列其實也是一樣的。

定理4:有兩行(列)相等,行列式值爲00

交換兩列後的行列式取反,但是我們發現兩次的行列式是相等的,所以x=x-x=xx=0x=0,行列式的值爲00

定理5:當有一行有公因子時,可以把公因子提出(相反,外面有個係數,乘到裏面某一行(列)也是可以的)。

定理6:當一行(列)爲另一行(列)的比例時,行列式的值爲00

將這一行的因子提出,和另一行相等,行列式的值爲00

定理7:某一行(列)的數字依次加上一些數字,那麼加上後的行列式可以拆爲兩個行列式相加:

如:矩陣AA{a1,1a1,2a2,1a2,2}\left\{ \begin{matrix} a_{1,1} &amp; a_{1,2}\\ a_{2,1} &amp; a_{2,2}\end{matrix} \right\}BB{a1,1a1,2a2,1+c1a2,2+c2}\left\{ \begin{matrix} a_{1,1} &amp; a_{1,2}\\ a_{2,1}+c_{1} &amp; a_{2,2}+c_{2}\end{matrix} \right\}CC{a1,1a1,2c1c2}\left\{ \begin{matrix} a_{1,1} &amp; a_{1,2}\\ c_{1} &amp; c_{2}\end{matrix} \right\},那麼det(B)=det(A)+det(C)det(B)=det(A)+det(C)

其實很簡單證,原本每個式子爲:(1)ka1,b1a2,b2a3,b3...ar,br(-1)^{k}a_{1,b_{1}}a_{2,b_{2}}a_{3,b_{3}}...a_{r,b_{r}},現在爲(1)ka1,b1a2,b2a3,b3...,(ai,bi+ci),a(i+1),bi+1...,ar,br(-1)^{k}a_{1,b_{1}}a_{2,b_{2}}a_{3,b_{3}}...,(a_{i,b_{i}}+c_{i}),a_{(i+1),b_{i+1}}...,a_{r,b_{r}}根據乘法分配率,這是可以拆開的。

定理8:某一行加上另爲一行的倍數,行列式的值不變。

很簡單,現在的行列式拆成原來的和某個奇怪的,某個奇怪的行列式存在一行是另一行的比例關係,所以這個行列式爲00,不影響值。


通過定理8,我們發現可以對行列式進行消元,類似高斯消元一樣,消成類似{a1,1a1,2a1,30a2,2a2,300a3,3}\left\{ \begin{matrix} a_{1,1}&#x27; &amp; a_{1,2}&#x27; &amp; a_{1,3}&#x27;\\ 0 &amp; a_{2,2}&#x27; &amp; a_{2,3}&#x27;\\ 0 &amp; 0 &amp; a_{3,3}&#x27;\\\end{matrix} \right\}的樣子

我們管這個矩陣叫上三角矩陣,很明顯,行列式的值就是對角線的值。

求法

其實生成樹的個數就是Kirchhoff矩陣的n1n-1階代數餘子式的行列式值結果。


一個矩陣(長寬相等)的n1n-1階餘子式刪去第ii行與第jj列。

假設我們又乘上了(1)1+2+3+...+ni+(1+2+3+...+nj)(-1)^{{1+2+3+...+n-i}+(1+2+3+...+n-j)}

就是n1n-1階的代數餘子式,我們可以化簡一下:(1)1+2+3+...+ni+(1+2+3+...+nj)=(1)n(n1)ij=(1)ij=(1)i+j(-1)^{{1+2+3+...+n-i}+(1+2+3+...+n-j)}=(-1)^{n(n-1)-i-j}=(-1)^{-i-j}=(-1)^{i+j}


Kirchhoff矩陣對於行列式又有一些性質:

定理1:Kirchhoff矩陣的兩個不同的n1n-1階代數餘子式的行列式相等。

我們設兩個餘子式刪的行是同一行,不同列(可以類似的推出不同行,同列,然後得證)

兩個矩陣爲Ci,j,Ci,k(j&lt;k)C_{i,j},C_{i,k}(j&lt;k)(刪去第ii行,第jjkk列)。

我們先將原本的矩陣變換一下,第jj列取反,然後加去除j,kj,k列的其他列乘上1-1的值,因爲每一行的值爲00,那麼第jj列的數字就等於00減去除kk列的其他所有數字,也就是第kk列的數字,然後我們將這一列在Ci,kC_{i,k}中一直移到k1k-1列(也就是Ci,jC_{i,j}kk列的位置),要交換kj1k-j-1次,那麼這個交換過程就乘上(1)kj(-1)^{k-j}次,然後(1)kj(1)i+k=(1)jk(1)i+k=(1)i+j(-1)^{k-j}*(-1)^{i+k}=(-1)^{j-k}*(-1)^{i+k}=(-1)^{i+j},所以我們就將Ci,kC_{i,k}轉換成Ci,jC_{i,j},且並沒改變值,得證。

定理2:Kirchhoff矩陣的行列式的值爲00

Why?Why?

因爲每一行的和爲0,在形成上三角的過程中,最後一行消掉和n1n-100,,但是最後一行的和爲00,所以nn個都是00


代碼

我們默認去掉第nn行第nn列。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define  N  30
using  namespace  std;
typedef  long  long  LL;
double  a[N][N],hehe=1e-8/*精度交換*/;
int  n,m;
void  zwap(double  &x,double  &y){double  z=x;x=y;y=z;}
double  guess()
{
	for(int  i=1;i<=n;i++)
	{
		int  what=i;//前i-1列不能交換
		while(fabs(a[what][i])<=hehe)what++;
		if(i==n+1)return  0;//沒辦法
		for(int  j=1;j<=n;j++)zwap(a[what][j],a[i][j]);
		for(int  j=i+1;j<=n;j++)
		{
			double  bi=a[j][i]/a[i][i];
			for(int  k=n+1;k>=i;k--)a[j][k]-=a[i][k]*bi;
		}
	}
	double  ans=a[1][1];
	for(int  i=2;i<=n;i++)ans=ans*a[i][i];
	return  fabs(ans);
}
int  main()
{
	scanf("%d%d",&n,&m);n--;
	for(int  i=1;i<=m;i++)
	{
		int  x,y;scanf("%d%d",&x,&y);
		a[x][x]+=1;a[y][y]+=1;
		a[x][y]-=1;a[y][x]-=1;
	}
	printf("%.0lf",guess());
	return  0;
}

輾轉相除法:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define  N  30
using  namespace  std;
typedef  long  long  LL;
LL  a[N][N];
int  n,m;
void  zwap1(int  &x,int  &y){int  z=x;x=y;y=z;}
void  zwap2(LL  &x,LL  &y){LL  z=x;x=y;y=z;}
LL  guess()
{
	int  now,cnt;
	for(int  i=1;i<=n;i++)
	{
		int  what=i;
		while(a[what][i]==0)what++;
		if(i==n+1)return  0;
		for(int  j=1;j<=n;j++)zwap2(a[what][j],a[i][j]);
		for(int  j=i+1;j<=n;j++)
		{
			now=i,cnt=j;//傳說中免交換神器 
			while(a[now][i])
			{
				LL  bi=a[cnt][i]/a[now][i];//輾轉相處 
				for(int  k=i;k<=n;k++)a[cnt][k]-=a[now][k]*bi;
				zwap1(now,cnt);
			}
			if(cnt!=i)for(int  k=i;k<=n;k++)zwap2(a[j][k],a[i][k]);//交換 
		}
	}
	LL  ans=a[1][1];
	for(int  i=2;i<=n;i++)ans=ans*a[i][i];
	return  abs(ans);
}
int  main()
{
	scanf("%d%d",&n,&m);n--;
	for(int  i=1;i<=m;i++)
	{
		int  x,y;scanf("%d%d",&x,&y);
		a[x][x]++;a[y][y]++;
		a[x][y]--;a[y][x]--;
	}
	printf("%lld\n",guess());
	return  0;
}

證明

根據柯西-比內公式(後面會有比較垃圾的證明)可得:

det(BBT)=USSdet(BU)det(BUT)=USS(det(BU))2det(B&#x27;B&#x27;^{T})=\sum_{U∈S}^{|S|}det(B&#x27;_{U})det(B&#x27;^{T}_{U})=\sum_{U∈S}^{|S|}(det(B&#x27;_{U}))^{2}

BB&#x27;表示的是,UU代表在mm中隨便選n1n-1個不重複數字的集合,SS表示所有UU的集合,BUB&#x27;_{U}就是n1n-1行選n1n-1列的一個(n1)(n1)(n-1)*(n-1)的集合,BUTB&#x27;^{T}_{U}n1n-1列,那麼我們會發現選n1n-1行不就是找到n1n-1條邊?

討論情況:

  1. 如果有幾條邊構成一個環,那麼這個detdet00

我們一步步把這幾行幾列換到一起,組成一個類似{101110011}\left\{ \begin{matrix} 1 &amp; 0 &amp; 1\\ -1 &amp; 1 &amp; 0\\ 0 &amp; -1 &amp; -1\\\end{matrix} \right\}的小矩陣,因爲每條邊都只有兩個數字,所以這33列這個行列式的這33列只能在這一選一個數字來乘,所以我們可以認爲這個矩陣的行列式等於這個矩陣去掉這三列的行列式乘以這個小矩陣的行列式,正負先不管,大家可以畫個圖模擬一下,因爲這三行這三列只能在這個小矩陣裏面選。

但是這個小矩陣我們消列,變成下三角,{a0...±aa±a...0............0...±a±a}\left\{ \begin{matrix} a &amp; 0 &amp; ... &amp; ±a\\ -a &amp; ±a &amp; ... &amp; 0\\ ... &amp; ... &amp; ... &amp;...\\ 0 &amp; ...&amp; ±a &amp; ±a\end{matrix} \right\}a=±1a=±1),我們發現先用第一列消最後一列,最後一列第二個又有數字,就用第二列消,也就是依次用第一列一直下去消,那麼我們模擬一下過程,當第一行消最後一列時,用aaaa時,最後一列加上第一列的取反,那麼最後一列第二個元素爲aa,如果用aaa-a,直接加,那麼最後一列的第二個數字等於a-a,怎麼這麼巧,消完後最後一列的第二個元素就等於第一個?其實仔細想想就知道,就是是從第二列開始去消也是,那麼最終會右下角的倒數兩行兩列就變成了{bcbc}(b=±1,c=±1)\left\{ \begin{matrix} b &amp; c \\ -b &amp; -c\end{matrix} \right\}(b=±1,c=±1),那麼最後一列就是倒數第二列的±1±1倍,行列式的值爲00

  1. 沒有環爲±1±1

我們已經把第nn行消掉,又沒有環,必然有一條邊是連向nn的,那麼我們這一列選的就是這條邊的另外一個端點,但是nn個點的圖n1n-1邊不就是棵樹嗎,而且一邊佔一列,我們可以認爲一條邊選一個點且選的點不重複,那麼就是在n1n-1列裏面選n1n-1個點。

我們會發現這個端點就是另外一些邊的端點,那麼這些邊又只能選另外一個端點,以此類推,這個行列式的有效選點只有一種,所以爲±1±1,再取個平方就是11

通過上面我們會發現Kirchhoff矩陣的n1n-1階代數餘子式的行列式值就是生成樹的個數!

柯西-比內公式

我是看度娘證明的,很不錯https://baike.baidu.com/item/Binet-Cauchy%E5%AE%9A%E7%90%86/8255247。

當然,參考鏈接有,裏面提到的Laplace展開定理我也不會,但是其中用到Laplace展開定理的地方我們都可以感性的理解。。。

我就是對它的一個補充說明

提一下,就是柯西比內公式講了

在這裏插入圖片描述

放上圖片,然後一一解釋:

在這裏插入圖片描述

一個444*4的單位矩陣爲{1000010000100001}\left\{ \begin{matrix} 1 &amp; 0 &amp; 0 &amp; 0\\ 0 &amp; 1 &amp; 0 &amp; 0\\ 0 &amp; 0 &amp; 1 &amp; 0\\ 0 &amp; 0 &amp; 0 &amp; 1\end{matrix} \right\},也就是對角線爲11,單位矩陣乘以任何矩陣都等於矩陣本身。

我們把M的第n+1,n+2…n+s行的第img 倍加到第k行去.(k=1,2…n),也就是用(A(I))+A(A*(-I))+AAA的位置全部變爲0,然後原本00的位置加上ABAB變成了CC,並且行列式的值並沒變。

下面的證明不理1-1


因爲前面的nn行只能選CC中的數字,且CCnnn*nBBsns*n,所以BB裏面的數字也不能選,只能選I(ss)-I(s*s)裏面的數字。

那麼det(N)=det(C)det(I)=det(AB)det(I)det(N)=det(C)*det(-I)=det(AB)*det(-I)

det(M)=det(N)=det(AB)det(I)det(M)=det(N)=det(AB)*det(-I)

MM進行分解,前nn行只能選AA中的數字,當n&gt;sn&gt;s時,選nn行必定有些選不到AA裏面選到了00,所以爲00,如果n=sn=s,那麼就是隻能選ABA、B裏面的數字了,也就是det(C)=det(AB)=det(A)det(B)det(C)=det(AB)=det(A)*det(B),但是n&gt;sn&gt;s時,我們就只能選nn列,那麼我們可以相應的知道I-I中只能選主對角線上(從左上到右下的對角線)的sns-n點,也就是in=ji-n=j(類似行等於列),那麼BB也就只能選其他行的nn個點,那麼也就是我們選AA的那些列的列數,也就得到了我們柯西-比內的基本結構了,但是我們並沒有理正負。


nsn≥s

det(C)det(I)det(C)*det(-I)的正負數:

det(I)=(1)sdet(-I)=(-1)^{s},而且I-I中的ss個點每個點都與CC中的nn個點組成逆序對數,也就是nsns,所以det(C)det(I)det(C)*det(-I)在理正負的情況下爲det(C)(1)(n+1)sdet(C)*(-1)^{(n+1)s}

det(M)det(M)分解後(因爲那個公式實在太難寫了QAQ)的正負號:

AABB的數字構不成逆序對數,那麼我們考慮I-I的,我們設選的AA的列數爲d1,d2,...,dnd_{1},d_{2},...,d_{n},那麼BB選的行數就爲d1+n,d2+n,...,dn+nd_{1}+n,d_{2}+n,...,d_{n}+n

我們設現在選的其中一個I-I主對角線的點的行列爲i,ji,j,那麼再看我們在A,BA,B選的2n2n個點,AA中的點的行數皆小於ii,當dk&gt;jd_{k}&gt;j時(1kn1≤k≤n)有逆序對,BB中的點列數皆小於jj,當di+n&lt;id_{i}+n&lt;i時有逆序對,拆開爲di+n&lt;j+n,di&lt;jd_{i}+n&lt;j+n,d_{i}&lt;j,發現當di&lt;jd_{i}&lt;jdi&gt;jd_{i}&gt;j都有一個逆序對,那麼I-I中每個點都有nn個逆序對,也就是n(sn)n(s-n)個,I-I自帶sns-n1-1,那麼就是(1)(n+1)(sn)(-1)^{(n+1)(s-n)}

證明(n+1)s=(n+1)(sn)(n+1)s=(n+1)(s-n)的奇偶性相等。

nn爲奇數,得證QMQ。

nn爲偶數,ss爲奇數偶數時,sssns-n的奇偶都是一樣的。

那麼也就是相等,所以柯西-比內公式得證。

不會Laplace展開定理就是煩QAQ。

小結

還是好多坑沒填QAQ。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章