文章目录
- 扩展欧几里得
- 线性同余方程 : a ∗ x ≡ b ( m o d m ) a*x\equiv b(mod \ m) a∗x≡b(mod m)求x
- 中国剩余定理 :一个解满足多个同余方程组。求x
- 高次同余方程 a x ≡ b ( m o d p ) a^x\equiv b(mod \ p) ax≡b(mod p) 求x
- 矩阵乘法: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)在 o ( l o g ( n ) ) 下 得 到 f ( n ) o(log(n))下得到f(n) o(log(n))下得到f(n)
- 高斯消元: 线 性 方 程 组 : 求 M 个 N 元 一 次 方 程 组 的 解 线性方程组:求M个N元一次方程组的解 线性方程组:求M个N元一次方程组的解
- 线性空间
- 线性基
扩展欧几里得
对 于 任 意 整 数 a , b , 存 在 一 对 整 数 x , y , 满 足 a x + b y = g c d ( a , b ) . 对于任意整数a,b,存在一对整数x,y,满足ax+by=gcd(a,b). 对于任意整数a,b,存在一对整数x,y,满足ax+by=gcd(a,b).
证明:
代码如下:
int exgcd(int a,int b,int &x,int &y){
// a>b a*x+b*y=gcd(a,b);
if(b==0){
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int z=x;
x=y;
y=z-(a/b)*y;
return d;
}
依照上面代码可求得特解:
x
0
,
y
0
x_0,y_0
x0,y0。
对于一般方程:
a
x
+
b
y
=
c
,
d
=
g
c
d
(
a
,
b
)
ax+by=c,d=gcd(a,b)
ax+by=c,d=gcd(a,b)如果满足有解的前提是:
d
∣
c
d|c
d∣c
不满足必然无解。
然后对于
a
x
+
b
y
=
c
ax+by=c
ax+by=c 的一组特解
x
=
(
c
/
d
)
∗
x
0
,
y
=
(
c
/
d
)
∗
y
x=(c/d)*x_0,y=(c/d)*y
x=(c/d)∗x0,y=(c/d)∗y。
事实上:
方程 a x + b y = c ax+by=c ax+by=c的通解可以表示为: x = c d x 0 + k b d , y = c d y 0 − k a d . ( k ∈ Z ) x=\frac{c}{d}x_0+k\frac{b}{d},y=\frac{c}{d}y_0-k\frac{a}{d}.(k\in Z) x=dcx0+kdb,y=dcy0−kda.(k∈Z)
例题Sumdiv
传送门
题意:
A
B
A^B
AB所有的约数之和
m
o
d
mod
mod
9901
9901
9901 ,数据范围(
1
<
=
A
,
B
<
=
5
∗
1
0
7
1<=A,B<=5*10^7
1<=A,B<=5∗107)
思路:将A分解质因数,可以表示为
A
=
P
1
c
1
P
2
c
2
P
3
c
3
.
.
.
.
P
n
c
n
A=P_1^{c_1}P_2^{c_2}P_3^{c_3}....P_n^{c_n}
A=P1c1P2c2P3c3....Pncn
所以
A
B
=
P
1
B
∗
c
1
P
2
B
∗
c
2
P
3
B
∗
c
3
.
.
.
.
P
n
B
∗
c
n
A^B=P_1^{B*c_1}P_2^{B*c_2}P_3^{B*c_3}....P_n^{B*c_n}
AB=P1B∗c1P2B∗c2P3B∗c3....PnB∗cn
约数之和为:
(
1
+
P
1
+
P
1
2
.
.
.
.
+
P
1
B
∗
c
1
)
(
1
+
P
2
+
P
2
2
.
.
.
.
+
P
2
B
∗
c
2
)
(
1
+
P
n
+
P
n
2
.
.
.
.
+
P
n
B
∗
c
n
)
(1+P_1+P_1^2....+P_1^{B*c_1})(1+P_2+P_2^2....+P_2^{B*c_2})(1+P_n+P_n^2....+P_n^{B*c_n})
(1+P1+P12....+P1B∗c1)(1+P2+P22....+P2B∗c2)(1+Pn+Pn2....+PnB∗cn)
然后就是等比数列求和:
1
−
P
1
c
1
∗
B
+
1
1
−
P
1
∗
1
−
P
2
c
2
∗
B
+
1
1
−
P
2
.
.
.
.
.
∗
1
−
P
n
c
n
∗
B
+
1
1
−
P
n
\frac{1-P_1^{c_1*B+1}}{1-P_1}*\frac{1-P_2^{c_2*B+1}}{1-P_2}.....*\frac{1-P_n^{c_n*B+1}}{1-P_n}
1−P11−P1c1∗B+1∗1−P21−P2c2∗B+1.....∗1−Pn1−Pncn∗B+1
这里需要注意一点:9901是质数,当
P
i
−
1
是
9901
的
倍
数
时
逆
元
就
不
适
用
啦
,
需
要
特
判
P_i-1是9901的倍数时逆元就不适用啦,需要特判
Pi−1是9901的倍数时逆元就不适用啦,需要特判
因为
(
P
i
−
1
)
(P_i-1)
(Pi−1) %
m
o
d
=
0
mod=0
mod=0 所以
P
i
P_i
Pi %
m
o
d
=
1
,
得
到
P
i
n
mod=1,得到P_i^n
mod=1,得到Pin%
m
o
d
=
1
mod=1
mod=1
此时
(
1
+
P
i
+
P
i
2
.
.
.
.
+
P
i
B
∗
c
i
)
=
(
1
+
1
1
+
1
2
+
.
.
.
+
1
B
∗
c
i
)
=
(
B
∗
c
i
+
1
)
(1+P_i+P_i^2....+P_i^{B*c_i})=(1+1^1+1^2+...+1^{B*c_i})=(B*c_i+1)
(1+Pi+Pi2....+PiB∗ci)=(1+11+12+...+1B∗ci)=(B∗ci+1)
代码如下:
#include<iostream>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=9901;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll qpow(ll a,ll x){
ll ans=1;
while(x){
if(x&1){
ans=(ans*a)%P;
}
a=(a*a)%P;
x>>=1;
}
return ans;
}
ll n;
void solve(){
ll A,B;
A=read();
B=read();
ll sum=1;
for(int i=2;i*i<=A;i++){
if(A%i==0){
ll cnt=0;
while(A%i==0){
cnt++;
A/=i;
}
// printf("P:%d cnt:%d\n",i,cnt);
if((i-1)%P==0){
sum=(sum*(cnt*B+1))%P;
}else{
ll ans=(qpow(i,cnt*B+1)-1+P)%P*qpow(i-1,P-2)%P;
sum=(sum*ans)%P;
}
}
}
if(A>1){
if((A-1)%P==0){
sum=(sum*(B+1))%P;
}else{
ll ans=(qpow(A,B+1)-1+P) %P *qpow(A-1,P-2)%P;
sum=(sum*ans)%P;
}
}
printf("%d\n",sum);
return ;
}
int main (){
// freopen("in.txt","r",stdin)
// freopen("out.txt","w",stdout);
solve();
getchar();
getchar();
return 0;
}
线性同余方程 : a ∗ x ≡ b ( m o d m ) a*x\equiv b(mod \ m) a∗x≡b(mod m)求x
给定
a
,
b
,
m
,
求
一
个
整
数
x
满
足
a
∗
x
≡
b
(
m
o
d
m
)
a,b,m,求一个整数x满足 a*x\equiv b(mod \ m)
a,b,m,求一个整数x满足a∗x≡b(mod m),或者无解。
令
a
∗
x
−
b
是
m
的
−
y
倍
a*x-b是m的-y倍
a∗x−b是m的−y倍,可得到
a
∗
x
−
b
=
−
y
∗
m
a*x-b=-y*m
a∗x−b=−y∗m
得到
a
∗
x
+
y
∗
m
=
b
a*x+y*m=b
a∗x+y∗m=b,这个方程就很熟悉了,就是扩展欧几里得。
求解即可。
例题:同余方程
传送门
题意很简单:求最小的正整数x,满足
a
∗
x
≡
1
(
m
o
d
b
)
a*x\equiv 1(mod \ b)
a∗x≡1(mod b)。保证x有解。
根据上面的推论得到
a
∗
x
+
b
∗
y
=
1
a*x+b*y=1
a∗x+b∗y=1
使用扩展欧几里得求得一个特解
x
0
x_0
x0。
然后通解
x
=
1
g
c
d
(
a
,
b
)
x
0
+
k
b
g
c
d
(
a
,
b
)
(
k
∈
Z
)
x=\frac{1}{gcd(a,b)} x_0+k\frac{b}{gcd(a,b)} \ (k\in Z)
x=gcd(a,b)1x0+kgcd(a,b)b (k∈Z).
必须保证
x
x
x有解,那么
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1
所以
x
=
x
0
+
k
∗
b
(
k
∈
Z
)
x=x_0+k*b \ (k\in Z)
x=x0+k∗b (k∈Z)
那其实
x
x
x就是(
x
0
x_0
x0%b+b)%b;
代码如下:
#include<iostream>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=9901;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll qpow(ll a,ll x){
ll ans=1;
while(x){
if(x&1){
ans=(ans*a)%P;
}
a=(a*a)%P;
x>>=1;
}
return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b,x,y);
ll z=x;
x=y;
y=z-(a/b)*y;
return d;
}
void solve(){
ll a,b;
ll x,y;
a=read();
b=read();
ll d=exgcd(a,b,x,y);
ll sum=(x%b+b)%b;
cout<<sum<<endl;
return ;
}
ll n;
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
// T=read();
// while(1)
solve();
getchar();
getchar();
return 0;
}
中国剩余定理 :一个解满足多个同余方程组。求x
这里的 t i t_i ti一定是有解的,因为 M i t i 与 m i 互 质 M_it_i与m_i互质 Miti与mi互质。
通解 x = x 0 + k ∗ m ( k ∈ Z ) x=x_0+k*m \ (k\in Z) x=x0+k∗m (k∈Z)
这个解也很能说明
例题:Strange Way to Express Integers
传送门
题意:
x ≡ r i ( m o d a i ) x\equiv r_i(mod \ a_i) x≡ri(mod ai).
思路:
考虑数学归纳法,来做这道题就好做了, 然后注意一个点,即是求t,这个求t值就是用到扩展欧几里得算法来实现他,然后我们要求得是的最小的他t.
代码如下:
// submitted by HNUST26
#include<iostream>
#define ll __int64
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=9901;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll qpow(ll a,ll x){
ll ans=1;
while(x){
if(x&1){
ans=(ans*a)%P;
}
a=(a*a)%P;
x>>=1;
}
return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b,x,y);
ll z=x;
x=y;
y=z-(a/b)*y;
return d;
}
int n;
void solve(){
while(scanf("%d",&n)!=EOF){
ll x0;
ll a1,r1,a2,r2;
ll l,d;
ll x,y;
scanf("%I64d %I64d",&a1,&r1);
l=a1; //最小公倍数
x0=r1; // 当前的最小解
int flag=0;
for(int i=1;i<=n-1;i++){
scanf("%I64d %I64d",&a2,&r2);
d=exgcd(l,a2,x,y);
ll c=r2-x0;
if(c%d){
flag=1;
continue;
}
ll t=a2/d;
// 求得最小的ans
ll ans=((c/d*x)%t+t)%t;
//叠加上去
x0=x0+l*ans;
l=(l*a2)/gcd(l,a2);
}
if(flag){
cout<<"-1"<<endl;
}else{
printf("%I64d\n",x0);
}
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
// T=read();
// while(1)
solve();
getchar();
getchar();
return 0;
}
高次同余方程 a x ≡ b ( m o d p ) a^x\equiv b(mod \ p) ax≡b(mod p) 求x
这里我们只展开一类来讲:
a
x
≡
b
(
m
o
d
p
)
a^x\equiv b(mod \ p)
ax≡b(mod p)
问题引入:
给定整数
a
,
b
,
p
a,b,p
a,b,p.其中a,p互质,求一个非负整数
x
x
x,使得
a
x
≡
b
(
m
o
d
p
)
a^x\equiv b(mod \ p)
ax≡b(mod p)
其实是BSGS算法。
可参考超详解证明数论篇(1)-最大公约数,素数筛,欧拉函数,同余,欧拉定理,BSGS
矩阵乘法: f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)在 o ( l o g ( n ) ) 下 得 到 f ( n ) o(log(n))下得到f(n) o(log(n))下得到f(n)
之前写过有关矩阵乘法的有关题解。
详解证明
高斯消元: 线 性 方 程 组 : 求 M 个 N 元 一 次 方 程 组 的 解 线性方程组:求M个N元一次方程组的解 线性方程组:求M个N元一次方程组的解
double a[15][15];
// 起始下标1 增广矩阵
bool guass(int row,int col){
for(int i=1;i<=row;i++){
int k=i;
// 获取第i列的最大值的行位置。
for(int j=i+1;j<=row;j++){
if(fabs(a[j][i])>fabs(a[k][i])){
k=j;
}
}
if(k!=i){
// 第i列的最大值的行与i行互换。
for(int j=1;j<=col;j++){
swap(a[i][j],a[k][j]);
}
}
for(int j=1;j<=row;j++){
if(i==j) continue;
double temp=a[j][i]/a[i][i];
for(int k=1;k<=col;k++){
a[j][k]-=a[i][k]*temp;
}
}
}
return true;
}
例题
球形空间产生器sphere
题意:在一个 n n n维里,给定 n + 1 n+1 n+1个点的坐标。求所有点都在一个球上的球心坐标。
思路:首先距离:设两个n为空间上的点A, B的坐标为 ( a 1 , a 2 , … , a n ) , ( b 1 , b 2 , … , b n ) (a_1, a_2, …, a_n), (b_1, b_2, …, b_n) (a1,a2,…,an),(b1,b2,…,bn),则 A B AB AB的距离定义为: d i s t = s q r t ( ( a 1 − b 1 ) 2 + ( a 2 − b 2 ) 2 + … + ( a n − b n ) 2 ) dist = sqrt( (a_1-b_1)^2 + (a_2-b_2)^2 + … + (a_n-b_n)^2 ) dist=sqrt((a1−b1)2+(a2−b2)2+…+(an−bn)2)
对于任意两个点 i 和 j i和j i和j有
( x − x i ) 2 + ( y − y i ) 2 + ( z − z i ) 2 + . . . . . . + = ( x − x j ) 2 + ( y − y j ) 2 + ( z − z j ) 2 . . . . (x-x_i)^2 + (y-y_i)^2+(z-z_i)^2+......+=(x-x_j)^2 + (y-y_j)^2+(z-z_j)^2.... (x−xi)2+(y−yi)2+(z−zi)2+......+=(x−xj)2+(y−yj)2+(z−zj)2....
化简得到: 2 ( x j − x i ) x + 2 ( y j − y i ) y + 2 ( z j − z i ) z + . . . + = ( x j 2 − x i 2 ) + ( y j 2 − y i 2 ) + ( z j 2 − z i 2 ) + . . . + . 2(x_j-x_i)x+2(y_j-y_i)y+2(z_j-z_i)z+...+=(x_j^2-x_i^2)+(y_j^2-y_i^2)+(z_j^2-z_i^2)+...+. 2(xj−xi)x+2(yj−yi)y+2(zj−zi)z+...+=(xj2−xi2)+(yj2−yi2)+(zj2−zi2)+...+.
于是可以用高斯消元来解决啦。依据题意这样的球心的是一定存在的,所有不用判断是否有无解
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
double a[15][15];
bool guass(int row,int col){
for(int i=1;i<=row;i++){
int k=i;
for(int j=i+1;j<=row;j++){
if(fabs(a[j][i])>fabs(a[k][i])){
k=j;
}
}
if(k!=i){
for(int j=1;j<=col;j++){
swap(a[i][j],a[k][j]);
}
}
for(int j=1;j<=row;j++){
if(i==j) continue;
double temp=a[j][i]/a[i][i];
for(int k=1;k<=col;k++){
a[j][k]-=a[i][k]*temp;
}
}
}
return true;
}
ll n,m;
double x[20];
double y[20];
void solve(){
n=read();
double x1,x2,y1,y2;
for(int i=1;i<=n;i++) cin>>x[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>y[j];
}
for(int j=1;j<=n;j++){
a[i][j]=2*(y[j]-x[j]);
a[i][n+1]+=(y[j]*y[j]-x[j]*x[j]);
}
for(int j=1;j<=n;j++){
swap(x[j],y[j]);
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=n+1;j++){
// printf("%.2f ",a[i][j]);
// }
// printf("\n");
// }
guass(n,n+1);
for(int i=1;i<n;i++){
printf("%.3f ",a[i][n+1]/a[i][i]);
}
printf("%.3f",a[n][n+1]/a[n][n]);
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
solve();
getchar();
getchar();
return 0;
}
开关问题
题意:有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)
思路:这里可以用
数据范围的 n < 32 n<32 n<32所有可以用 二进制来表示,
然后 a [ N ] a[N] a[N]就是一个增广矩阵。(稍微解释一下 a [ i ] = 1010 1 ( 2 ) a[i]=10101_{(2)} a[i]=10101(2) 意思就是第i个灯的状态可以被第2个开关或第4个开关影响) 对于( a [ i ] a[i] a[i]& 1 1 1就是结果矩阵)
代码如下
#include<iostream>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll n,m;
int a[100];
void solve(){
int n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i++){
a[i]^=read();
a[i]|=(1<<i);
}
int x,y;
while(~scanf("%d %d",&x,&y) and x and y){
a[y]|=(1<<x);
}
int ans=1;
for(int i=1;i<=n;i++){
//最左边的1
for(int j=i;j<=n;j++){
if(a[j]>a[i]) swap(a[j],a[i]);
}
if(a[i]==1){
// 000001 无解的情况
ans=0;
break;
}
if(a[i]==0){
// 下面的行全是0 此时主元个数 i-1 自由元个数n-i+1;
ans=(1<<(n-i+1));
break;
}
//找到最左边的1,只执行一次
for(int j=n;j>=1;j--){
if((a[i]>>j)&1){
// 行数的遍历
for(int k=1;k<=n;k++){
if(k!=i and ( (a[k]>> j) & 1 ) ) a[k]^=a[i];
}
break;
}
}
}
if(ans==0){
printf("Oh,it's impossible~!!\n");
}else{
printf("%d\n",ans);
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T=read();
while(T--)
solve();
getchar();
getchar();
return 0;
}
线性空间
例题:装备购买
传送门
题意:
脸哥最近在玩一款神奇的游戏,这个游戏里有
n
n
n 件装备,每件装备有
m
m
m 个属性,用向量
z
i
(
a
j
,
.
.
.
.
.
,
a
m
)
z_i(a_j ,.....,a_m)
zi(aj,.....,am) 表示
(
1
<
=
i
<
=
n
;
1
<
=
j
<
=
m
)
(1 <= i <= n; 1 <= j <= m)
(1<=i<=n;1<=j<=m),每个装备需要花费$ c_i$,现在脸哥想买一些装备,但是脸哥很穷,所以总是盘算着
怎样才能花尽量少的钱买尽量多的装备。对于脸哥来说,如果一件装备的属性能用购买的其他装备组合出(也就是
说脸哥可以利用手上的这些装备组合出这件装备的效果),那么这件装备就没有买的必要了。严格的定义是,如果
脸哥买了 zi1,…zip这 p 件装备,那么对于任意待决定的 zh,不存在
b
1
,
.
.
.
.
,
b
p
b1,....,bp
b1,....,bp 使得
b
1
z
i
1
+
.
.
.
+
b
p
z
i
p
=
z
h
(
b
是
实
数
)
b1zi1 + ... + bpzi p = zh(b 是实数)
b1zi1+...+bpzip=zh(b是实数),那么脸哥就会买 zh,否则 zh 对脸哥就是无用的了,自然不必购买。举个例子,z1 =(1; 2;
3);z2 =(3; 4; 5);zh =(2; 3; 4),b1 =1/2,b2 =1/2,就有 b1z1 + b2z2 = zh,那么如果脸哥买了 z1 和 z2
就不会再买 zh 了。脸哥想要在买下最多数量的装备的情况下花最少的钱,你能帮他算一下吗?
思路:求矩阵秩,然后在采取策略面前,使用优先选择价格最小的。
求矩阵秩模板:
long double a[N][N];
int p[N];
void Guass(int n,int m){
int cnt=0;
int sum=0;
rep(i,1,n){
rep(j,1,m){
if(abs(a[i][j])>eps){
if(!p[j]){
p[j]=i;
cnt++;
break;
}else{
ld temp=a[i][j]/a[p[j]][j];
rep(k,j,m){
a[i][k]-=temp*a[p[j]][k];
}
}
}
}
cout<<cnt<<endl;//秩
return ;
}
AC代码如下:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll P=1e9+7;
const double eps=1e-6;
ll read(){
ll s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
struct node{
ld col[501];
int cost;
}row[501];
bool cmp(node a,node b){
return a.cost<b.cost;
}
int p[501];
void Guass(int n,int m){
int cnt=0;
int sum=0;
rep(i,1,n){
rep(j,1,m){
if(abs(row[i].col[j])>eps){
if(!p[j]){ //第j列 在p[j]行
p[j]=i;
cnt++;
sum+=row[i].cost;
break;
}else{
ld temp=row[i].col[j]/row[p[j]].col[j];
rep(k,j,m){
row[i].col[k]-=temp*row[p[j]].col[k];
}
}
}
}
// rep(j,1,n){
// rep(k,1,m){
// cout<<row[j].col[k]<<" ";
// } cout<<endl;
// } cout<<endl;
}
cout<<cnt<<" "<<sum<<endl;
return ;
}
void solve(){
int n,m;
n=read();m=read();
rep(i,1,n){
rep(j,1,m){
scanf("%Lf",&row[i].col[j]);
}
}
rep(i,1,n){
scanf("%d",&row[i].cost);
}
sort(row+1,row+1+n,cmp);
Guass(n,m);
return ;
}
int main (){
solve();
getchar();
getchar();
return 0;
}
例题:XOR
题意:
思路: 异或操作也是满足线性无关。
对异或矩阵进行变换,化成阶梯型矩阵就好了。 那么他的秩为t,那么 2 t 2^t 2t就是该异或矩阵得到的所有可能,当然还有0的情况,需要特判。
ACcode
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=1e5+10;
const ll P=1e9+7;
ull read(){
ull s = 0, f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * f;
}
using namespace std;
ll n,m;
ull a[N];
void solve(int T){
n=read();
rep(i,1,n) scanf("%I64u",&a[i]);
int t=n;
bool zero=0;
rep(i,1,n){
rep(j,i+1,n){
if(a[j]>a[i]) swap(a[i],a[j]);
}
if(a[i]==0){
zero=1;
t=i-1;
break;
}
for(int k=63;k>=0;k--){
if(a[i]>> k&1){
rep(j,1,n){
if(i!=j and a[j] >> k & 1){
a[j]^=a[i];
}
}
break;
}
}
}
ull maxn=1ull <<t;
printf("Case #%d:\n",T);
ll q;
q=read();
while(q--){
ull k,ans=0;;
scanf("%I64u",&k);
if(zero) k--;
if(k>=maxn) printf("-1\n");
else{
// for(int i=t-1;i>=0;i--){
// if(k >> i & 1) ans^=a[t-i];
// }
int ind=t;
while(k){
if(k%2==1){
ans^=a[ind];
}
k/=2;
ind--;
}
printf("%I64u\n",ans);
}
}
return ;
}
int main (){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
T=read();
rep(i,1,T)
solve(i);
getchar();
getchar();
return 0;
}
线性基
参考博客Yveh:[学习笔记]线性基
板子: 插入,合并,查询,最小最大,第k小
// 线性基 板子
struct L_B{
// d[i] 二进制最高位是第i位. p[i]就是第2^i个小的数是p[i]
long long d[61],p[61]; // 顶值1e18
int cnt; // p数组有效个数
L_B()
{
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
cnt=0;
}
//插入
bool insert(long long val)
{
for (int i=60;i>=0;i--)
if (val&(1ll<<i))
{
if (!d[i])
{
d[i]=val;
break;
}
val^=d[i];
}
return val>0;
}
//得到最大值
long long query_max()
{
long long ret=0;
for (int i=60;i>=0;i--)
if ((ret^d[i])>ret)
ret^=d[i];
return ret;
}
//得到最小值
long long query_min()
{
for (int i=0;i<=60;i++)
if (d[i])
return d[i];
return 0;
}
// 题目中问到第k小的数是多少时,使用p数组
void rebuild()
{
for (int i=60;i>=0;i--)
for (int j=i-1;j>=0;j--)
if (d[i]&(1ll<<j))
d[i]^=d[j];
for (int i=0;i<=60;i++)
if (d[i])
p[cnt++]=d[i];
}
// 找第几小的值
long long kthquery(long long k)
{
int ret=0;
if (k>=(1ll<<cnt))
return -1;
for (int i=60;i>=0;i--)
if (k&(1ll<<i))
ret^=p[i];
return ret;
}
};