3.a^b%c(快速幂)
对于任何一个整数的模幂运算
a^b%c
对于b我们可以拆成二进制的形式
b=b0+b12+b222+…+bn*2n
这里我们的b0对应的是b二进制的第一位
那么我们的a^b运算就可以拆解成
ab0*ab12…*a(bn*2n)
对于b来说,二进制位不是0就是1,那么对于bx为0的项我们的计算结果是1就不用考虑了,我们真正想要的其实是b的非0二进制位
那么假设除去了b的0的二进制位之后我们得到的式子是
a(bx*2x)…a(bn2^n)
这里我们再应用我们一开始提到的公式,那么我们的a^b%c运算就可以转化为
(a(bx*2x)%c)…(a(bn*2n)%c)
这样的话,我们就很接近快速幂的本质了
(a(bx*2x)%c)…*(a(bn*2n)%c)
我们会发现令
A1=(a(bx*2x)%c)
…
An=(a(bn*2n)%c)
快速幂
typedef long long ll;
ll quick(ll a,ll b,ll c)
{
ll ans=1;
a=a%c;
while(b!=0)
{
if(b&1) ans=(ans*a)%c;
b>>=1;
a=(a*a)%c;
}
return ans;
}
4.(a*b)%p(快速乘)
inline ll mult_mod(ll a, ll b, ll m)
{
ll res = 0;
while(b){
if(b&1) res = (res+a)%m;
a = (a+a)%m;
b >>= 1;
}
return res;
}
5.位运算:
1<<n =2^n, n<<1 =2n;
n>>1 == n除以2向下取整
b在二进制下有k位:k=log2(b+1)的向下取整(取整函数为点2);
取出整数n在二进制表示下第k位 (n>>k)& 1;
对整数n在二进制表示下的第k位取反 n^(1<<k);
对整数n在二进制表示下的第k位赋值1 n|(1<<k);
对整数n在二进制表示下的第k位赋值0 n|(~(1<<k));
*6.快速幂:a(2(i))=(a(2(i-1)))^2;
7.快速乘:a*2^i = 2^(i-1)2a
8.最短Hamilton路径(二进制状态压缩)
给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
二进制上的数代表一个点的状态,取(1)或不取(0)。题目让求从点1到n的最短汉密顿路径,即经过每个点一次,这时的状态用二进制表示就是 (1<<n)-1 (n个1)。用dp[i][j]表示在状态 i 下,从1到 j 的最短汉密顿路径。
dp[i][j]可由上一个状态(上一状态就是把 j从当前状态中去掉)dp[i^(1<<(j-1))][k]得到,其中保证k是中存在的点,即 (i>>k)&1。
表示 i 的第 k 位是1,即经过点 k。 注意是 i>>k 不是 i<<k
则状态转移方程为:dp[i][j]=min{dp[i^(1<<j)][k]+Map[k][j]}(k=1~n); 其含义就是枚举到达点j之前的前一个点k,取其最短;(用k点中转,逐步缩短)
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define Inf 0x3f3f3f
#define ll long long
using namespace std;
int dp[(1<<20)+5][25];
int maze[25][25];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&maze[i][j]);
memset(dp,Inf,sizeof(dp));
dp[1][0]=0;
for(int i=1;i<=(1<<n)-1;i++) //二进制枚举各种状态
{
for(int j=0;j<n;j++)
{
if((i>>j)&1)//如果i的第j位是1,也就是如果经过点j
{
for(int k=0;k<n;k++)
{
if((i>>k)&1)//如果i的第k位是1,也就是如果经过点k
{
dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+maze[k][j]);//使用k点中转,取最小值
}
}
}
}
}
printf("%d\n",dp[(1<<n)-1][n-1]);
return 0;
//第一个for循环:取各种状态; for(int i=1;i<=(1<<n)-1;i++)
//第二个for循环:取状态的一个点;
//第三个for循环:取状态的另一个点,作为中转点,压缩距离
}
9,二进制枚举子集
// 枚举范围
for(int i=0;i<(1 << n);i++){
// 列出变化变量
int ...;
// 遍历每一位
for(int j=0;j<n;j++){
if(i & (1 << j)){ 这里判断二进制 i 从右数第 j + 1 位是否为 1
// 如果满足状态,遍历随着变化
}
}
//当遍历完一种情况,累积量
if(...)
...;
}
10.二维前缀和:直接暴力做前缀和,再暴力枚举右下节点即可,注意常数。
11.差分数组 把一个区间的操作转化为左,右俩个端点上的操作,再通过前缀和得到原问题的解
例:建立一个数组C,初始化全为0,将下标a+1到b-1的位置全部-1。用一个数组d,d[a+1]-1,d[b]+1(“身高减小1的影响从a+1开始到b结束”)时间复杂度o(n+m) ,map判断是否重复。俩种做法实现的功能一样。
map<pair<int,int>, bool>existed;
int c[maxn], d[maxn];
int main(){
int n, p, h, m;
cin>>n>>p>>h>>m;
for(int i = 1; i <= m; i++){
int a, b; cin>>a>>b;
if(a > b)swap(a, b);
if(existed[make_pair(a,b)])continue;
d[a+1]--; d[b]++;
existed[make_pair(a,b)] = 1;
}
for(int i = 1; i <= n; i++){
c[i] = c[i-1]+d[i];
cout<<h+c[i]<<'\n';
}
12.约数和公式 约数和公式:
对于已经分解的整数A=(p1k1)*(p2k2)*(p3k3)*…*(pnkn)
有A的所有因子之和为
S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)
13.二分求等比
先分治+快速幂
1)若n为奇数,一共有偶数项,则:
1 + p + p^2 + p^3 +…+ p^n = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +…+ p^(n/2) * (1+p^(n/2+1))
= (1 + p + p^2 +…+ p^(n/2)) * (1 + p^(n/2+1))
上式红色加粗的前半部分恰好就是原式的一半,后半部分递归求解即可。
(2)若n为偶数,一共有奇数项,则:
1 + p + p^2 + p^3 +…+ p^n = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +…+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)
= (1 + p + p^2 +…+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);
LL power(LL a,LL b)
{
LL ans = 1;
a %= M;
while(b)
{
if(b & 1)
{
ans = ans * a % M;
b--;
}
b >>= 1;
a = a * a % M;
}
return ans;
}
LL sum(LL a,LL n)
{
if(n == 1) return a;
LL t = sum(a,n/2);
if(n & 1)
{
LL cur = power(a,n/2+1);
t = (t + t * cur % M) % M;
t = (t + cur) % M;
}
else
{
LL cur = power(a,n/2);
t = (t + t * cur % M) % M;
}
return t;
}