快速幂取模的思路:快速幂实现的最基本的理论就是我们离散课上或者数论中学过的一条公式推出的引理。
引理:积的取余等于取余的积的取余。a * b % 3=(a%3) * (b%3) % 3
再在这条引理的基础之上,对指数型数据进行拆分以及合并,从而得到我们用的快速幂算法
举个例子
2^6,mod = 3
2^6%3
第一步
设 x=(2%3)
设 y= 6 (二进制为110)
第二步
x=x * x%3
y=6/2=3(二进制为11)
第三步
由于y为奇数
设 ans=ans * x%3(ans用来储存)
y=y-1
x=x * x%3;
y=y/2=1
最后一步
由于y为奇数
ans=ans * x%3
y=y-1=0
ans就是结果
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 3
ll qumi(ll x, ll y)
{
ll ans=1;
x=x%mod;
while(y)
{
if(y&1)//判断奇偶
ans=x*ans%mod;
x=x*x%mod;
y>>=1;//y/=2
}
return ans;
}
int main()
{
cout << qumi(2,6) << endl;
return 0;
}
一道例题
题目描述
BLUESKY007鸽鸽非常喜欢构造题,但这不是一道构造题。
BLUESKY007鸽鸽有一个n×m的矩阵,矩阵中每个格子都填有一个不超过x的自然数。Venn喜欢折腾,于是他拿起BLUESKY007的矩阵玩耍,但是他一不小心把矩阵摔在了地上,导致里面的数字都不见了。
Venn希望在BLUESKY007回来之前,恢复整个矩阵,但是他忘记了矩阵里面填了什么数字了。但是,他记得矩阵每一行的数字异或和是多少,也记得每一列数字异或和是多少。
现在,Venn想知道有多少种矩阵可能是原来的矩阵。两个矩阵不同,当且仅当他们至少有一个位置填的数字不同。
由于这个答案实在是太大了,你只需要求出他对p取模的值即可。
输入描述:
第一行一个正整数T,表示数据组数。
对于每组数据,首行有四个正整数n,m,x,p,代表的意义见题目描述。
接下来一行n个整数,其中第i个数字表示第i行的异或和。
接下来一行m个整数,其中第i个数字表示第i列的异或和。
输出描述:
输出包含T行
第i行包含一个整数代表第i组数据的答案。
示例1
输入
复制
1
2 2 1 998244353
0 0
0 0
输出
复制
2
考虑在x=1时,对于每个格子可以填0/1。那么所有的格子对应的数都只有一个二进制位。当所有行异或和的异或和等于所有列异或和的异或和时,在前n−1行m−1列的格子随意的填数,第n行和第m列总有唯一合法的填数方案。
考虑在x=2^k−1时,对于每个格子对应的数有k个二进制位,且0/1不受限制,此时相比于x=1,k个二进制位相互独立,所以在前n−1行m−1列的格子随意的填数,第n行和第m列也总有唯一合法的填数方案。
考虑当所有行异或和的异或和不等于所有列异或和的异或和时,矩阵填数一定不合法,此时答案为0。否则,矩阵内前n−1行m−1列可以随意填数,每个位置有x+1种填数方案,此时答案为(x+1)^{(n-1)(m-1)}。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
int work(long long x,long long y,long long mod)
{
int ans=1%mod;
x=x%mod;
while(y)
{
if(1&y)
{
ans=ans*x%mod;
}
x=x*x%mod;
y>>=1;
}
return ans;
}
long long t,n,m,x,p;
long long hh[maxn],lh[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
long long nh=0,ml=0;
scanf("%lld%lld%lld%lld",&n,&m,&x,&p);
for(int i=1; i<=n; i++)
{
scanf("%lld",&hh[i]);
nh^=hh[i];
}
for(int i=1; i<=m; i++)
{
scanf("%lld",&lh[i]);
ml^=lh[i];
}
if(nh!=ml)
{
printf("0\n");
continue;
}
long long ans=work(++x,n-1,p);
ans=work(ans,m-1,p);
printf("%lld\n",ans);
}
return 0;
}