快速幂,快速乘,位运算,最短Hamilton路径(二进制状态压缩),二进制枚举子集,前缀和,差分数组,约数和公式,二分求等比

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; 
} 
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值