来自acwing的题目:89. a^b - AcWing题库
题目描述acwing.com/problem/content/92/
求 a 的 b 次方对 p 取模的值。
输入格式
三个整数 a,b,pa,b,p ,在同一行用空格隔开。
输出格式
输出一个整数,表示a^b mod p
的值。
数据范围
0≤a,b≤10e9
1≤p≤10e9
输入样例:
3 2 7
输出样例:
2
这道题目是非常经典的快速幂,当然也可以说是位运算题目,具体的做法是:我们可以把某个数拆分,a或者b都可以,这里我们以b为例;
某个数的二进制表示可以为加法形式
这里的a1,a2...an表示二进制位1的位置,比如5的二进制为101,则a1=0,a2=2
以此推类,所有数都可以这样表示,那么a的b次方,讲b携程如上形式后,我们发现,还可以拆分开。幂指数的加法拆分后就变成了乘法,我们拿2与7举例(特殊例子)
这里的指数换成2进制后,可以进一步化简成后面形式,我们发现只要幂二进制向左移动一位,底数相当于自增一倍,但是二进制为0的地方,虽然自增了,但是答案却没记录进去。于是依据这个幂减半,底数倍增的原理,我们可以使用二进制或者是折半的方法来快速求出幂
代码如下:
#include<iostream>
using namespace std;
typedef long long LL;//把longlong定义为LL
int main()
{
LL a,b,p,ans=1;//ans记录答案
cin>>a>>b>>p;
while(b)//当b为0时说明已经结束,幂已经算完
{
if(b&1) ans=(ans*a)%p;//只有当二进制为1时才会乘入答案
a=(a*a)%p;//每次不管幂的二进制是不是一都要倍增
b>>=1;//每次把幂减半,写成/2也可以
}
cout<<ans%p<<endl;//这里是为了特判当b为0时,我们的a始终为1,但是p为1时
//答案应该为0,如果不进行最后这一步,答案始终为1造成错误
}
来自acwing的题目:acwing.com/problem/content/92/
题目描述
求 a 乘 b 对 p 取模的值。
输入格式
第一行输入整数aa,第二行输入整数bb,第三行输入整数pp。
输出格式
输出一个整数,表示a*b mod p
的值。
数据范围
1≤a,b,p≤10181≤a,b,p≤1018
输入样例:
3
4
5
输出样例:
2
在上文我们已经说过任何数可以写成2的幂的加法形式,于是两个数相乘我们也可以把其中一个数分解为2的幂的加法形式,然后分开乘法即可,这里我们使用3与5举例
我们发现还是当遇到5二的进制时,答案加上3乘以2的倍数,这个倍数仍然是自增的;
代码如下:
#include<iostream>
using namespace std;
typedef long long LL;
int main()
{
LL a,b,p;
cin>>a>>b>>p;
LL ans=0;//存答案
while(b)
{
if(b&1) ans=(ans+a)%p;//当b遇到二进制为1,说明可以加上递增的数,注意答案是加上的
a=(a*2)%p;//a每次乘以2递增,因为不是幂的形式,不是底数倍增,这里要注意
b>>=1; //仍然是b折半,两种形式都可
}
cout<<ans<<endl;//输出答案这里不需要考虑%p,大家可以自行思考
}
来自acwing的题目:https://www.acwing.com/activity/content/problem/content/325/
这道题目有些难度,涉及到状态压缩dp,不如说状态压缩dp用到了位运算,所以这道题目才能出现在这里,感觉困难的同学可以往后再看;
这道题目给了我们一个无向连通图,让我们能否找到一条最短路径从0走到n-1,这道题目不需要考虑可行性,因为我们的图每个节点都有走向其他节点的选择,所以从某个点走到最后一个点的所有情况就是20!相当于全排列;
所以这道题目肯定不会是图论的题目,不是图论就是dfs问题了,但是20!的阶乘又肯定算不出来,还是得dp,那么我们来看看如何dp;
我们可以发现这样一个现象,假如我们只有5个点,那么以4为终点的路径个数:
从0-4总共有6条路径,但是肯定只有一条是最优路径,因为我们只需要总权值最小的那条路;
这个时候我们假设以3为结尾,会有以下路径:
这个时候再从3-5,所有路径有且只有一条是最短的,这个时候我们在与上面的比较,我们发现只会有一条最短的路径保存。
那么以此推类,我们找到只有4个节点的图,会发现只可能有一种情况最短,比如我们使用01234五个点,只会有一条路径最短,01235也只会有一条最短,这样再加入其他节点时,我们只需要考虑没有这个节点的其他包含5个节点的长度加上这个长度哪个最短即可求出以此节点为最后节点的长度为n(2-20)的最短距离,dp过后,所有情况的最短距离决出胜负。
既然已经确定是dp问题,如何表示呢,我们用二进制来表示经过哪些节点,比如1011表示经过0,1,3,节点,一次推类到20个节点,那么情况就有1<<20中情况;
状态转移我们以已经遍历过的节点为例查找,假设某条边未被加入,我们用之前的状态和两节点间的距离相加与现在的距离比较,如果更小即可更新。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20,M=1<<N;
int weight[N][N],ans[M][N];//weight存储权值,ans存储答案,M为1-20个节点的所有情况
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>weight[i][j];
memset(ans,0x3f3f3f3f,sizeof(ans));//首先把所有距离都设为无穷大
ans[1][0]=0;
for(int i=0;i<1<<n;i++)//首先遍历所有情况
for(int j=0;j<n;j++)//遍历所有位(查找遍历已经过节点的位置)
if(i>>j&1)
for(int k=0;k<n;k++)//接下来遍历 其他节点进行判断
if(i>>k&1)//能否进行更优解的更换,这里直接改变,只要最小值即可
ans[i][j]=min(ans[i][j],ans[i-(1<<j)][k]+weight[k][j]);//不要这里是查找
//已经遍历过的节点并寻找最小值,所以应把当前节点减去;
cout<<ans[(1<<n)-1][n-1]<<endl;//输出答案即可
}
至此完结撒花