刷穿李煜东蓝书(读书笔记) 更新中

0x00 基本算法

0x01 位运算

a^b

题目描述
求 a 的 b 次方对 p 取模的值,其中 0≤a,b≤10^9 , 0<p≤10^9

输入
三个用空格隔开的整数a,b和p。

输出
一个整数,表示a^b mod p的值。

样例输入
2 3 9

样例输出
8

思路
普通求幂时间复杂度为O(b),会TLE
设b的二进制表示有k位,ci为0或1,则

\[b = \displaystyle\sum_{i=0}^{k-1} c_i * 2^i \]

\[a^b = a^{\displaystyle\sum\limits_{i=0}^{k-1} c_i*2^i} = \prod_{k=0}^{k-1}a^{c_{i}*2^i} = \prod_{k=0}^{k-1}{(a^{2^i})}^{c_i} \]

\[a^{2^i} = a^{2^{i-1}}*a^{2^{i-1}} \]

所以计算k-1次就可以求出答案,时间复杂度优化到O(logb)

还可以用分治的思想

\[a^b = \left\{ \begin{aligned} & a^{\frac b2}*a^{\frac b2} &(b为偶数) \\ & a^{\frac {b - 1}2}*a^{\frac {b-1}2}*a& (b为奇数)\\ \end{aligned} \right. \]

代码

非递归写法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_power(ll a, ll b, ll p){ // 快速幂
    ll ans = 1 % p;
    while(b){
        if(b & 1) ans = a*ans % p;
        a = a*a % p;
        b >>= 1;
    }
    return ans;
}
int main(){
    
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_power(a,b,p);
    cout<<ans;
}

递归写法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_power(ll a, ll b, ll p){ //快速幂
    if(b == 0) return 1 % p;
    ll res = quick_power(a,b>>1,p)%p;
    if(b&1) return (res * res % p) * a % p; 
    else return  res * res % p;

}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_power(a, b, p);
    cout<<ans;
}

64位整数乘法

题目描述
求a乘b对p取模的值,其中1≤a,b,p≤1018

输入
输入3个long long型整数,a,b,p

输出
输出a*b%p的值

样例输入
250182048980811753
413715569939057660
133223633696258584

样例输出
19308689043391716

思路
与快速幂类似

\[b = \sum\limits_{i=0}^{k-1} c_i*2^i a*b = a*\sum\limits_{i=0}^{k-1} c_i*2^i = \sum\limits_{i=0}^{k-1} c_i*(a*2^i) \]

\[a*2^i = a*2^{i-1}*2 \]

同样也可以用分治的思想

\[a*b = \left\{ \begin{aligned} & a*{\frac b2} + a*{\frac b2}&(b为偶数) \\ & a*{\frac {b-1}2} + a*{\frac {b-1}2}+a& (b为奇数)\\ \end{aligned} \right. \]

代码

非递归写法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_mul(ll a, ll b, ll p){ // 快速乘
    ll ans = 0;
    while(b){
        if(b&1) ans = (ans + a) % p;
        a = a*2%p;
        b >>= 1;
    }
    return ans;
}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_mul(a,b,p);
    cout<<ans;
}

递归写法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_mul(ll a, ll b, ll p){ // 快速乘
    if(b == 0) return 0;
    ll res = quick_mul(a,b>>1,p) %p;
    if(b & 1) return ((res+res)%p+a) %p;
    else return (res+res)%p; 
}
int main(){
    ll a,b,p;
    cin>>a>>b>>p;
    ll ans = quick_mul(a,b,p);
    cout<<ans;
}

当然这道题可以python过

print(int(input()) * int(input()) % int(input()))

Raising Modulo Numbers

题目描述
People are different. Some secretly read magazines full of interesting girls' pictures, others create an A-bomb in their cellar, others like using Windows, and some like difficult mathematical games. Latest marketing research shows, that this market segment was so far underestimated and that there is lack of such games. This kind of game was thus included into the KOKODáKH. The rules follow:

Each player chooses two numbers Ai and Bi and writes them on a slip of paper. Others cannot see the numbers. In a given moment all players show their numbers to the others. The goal is to determine the sum of all expressions AiBi from all players including oneself and determine the remainder after division by a given number M. The winner is the one who first determines the correct result. According to the players' experience it is possible to increase the difficulty by choosing higher numbers.

You should write a program that calculates the result and is able to find out who won the game.

输入
The input consists of Z assignments. The number of them is given by the single positive integer Z appearing on the first line of input. Then the assignements follow. Each assignement begins with line containing an integer M (1 <= M <= 45000). The sum will be divided by this number. Next line contains number of players H (1 <= H <= 45000). Next exactly H lines follow. On each line, there are exactly two numbers Ai and Bi separated by space. Both numbers cannot be equal zero at the same time.

输出
For each assingnement there is the only one line of output. On this line, there is a number, the result of expression
(A1B1+A2B2+ ... +AH^BH)mod M.

样例输入
3
16
4
2 3
3 4
4 5
5 6
36123
1
2374859 3029382
17
1
3 18132

样例输出
2
13195
13

思路
套快速幂模板就行

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quick_power(ll a, ll b, ll p){ 
    ll ans = 1 % p;
    while(b){
        if(b & 1) ans = ans*a%p;
        a = a*a%p;
        b >>= 1;
    }
    return ans;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        ll p;
        cin>>p;
        ll ans = 0;
        int num;
        cin>>num;
        for(int i = 0; i < num; i++){
            ll a,b;
            cin>>a>>b;
            ans = (ans + quick_power(a,b,p)) % p;
        }
        cout<<ans<<endl;
    }
}

最短Hamilton路径

题目描述
给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入
第一行一个整数n。

接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为a[i,j])。

对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。

输出
一个整数,表示最短Hamilton路径的长度。

样例输入
4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0

样例输出
4

思路
二进制状态压缩dp
用一个n位的二进制数表示哪些点被经历过(该数的第i位为1表示第i个点被经历过)
dp[i][j] 表示"点被经历过"的状态为i,当前位置为j点的最短路径。
由于当前位置在j,设是从k走到j,则前一状态一定没有经历过j,于是前一状态的经历的点可以表示成(i xor (1 << j)),即将i的第j位置成0。
所以状态转移方程为

\[dp[i][j] = min(dp[i \; xor\; (i << j)][k] \;+ \; weight[k][j])\; (0<k<n) \]

当然由dp[i][j]的意义可知,i的第j位一定要是1,所以只有(i >> j & 1)和((i xor 1 << j)>> k & 1 )为真才转移

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int weight[30][30];
int dp[1<<20][30];
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(dp,0x3f,sizeof(dp)); //将dp中所有状态初始化成0x3f3f3f3f(INF)
    dp[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^(1<<j) >> k & 1){ 
                        dp[i][j] = min(dp[i][j],dp[i^(1<<j)][k]+weight[k][j]);
                    }
            }
    }
    cout<<dp[(1 << n) - 1][n-1]<<endl;
}

起床困难综合征

题目描述

背景
21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。正是由于 drd 的活动,起床困难综合症愈演愈烈,以惊人的速度在世界上传播。为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。

描述

历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。具体说来,drd 的防御战线由 n扇防御门组成。每扇防御门包括一个运算op和一个参数t,其中运算一定是OR,XOR,AND中的一种,参数则一定为非负整数。如果还未通过防御门时攻击力为x,则其通过这扇防御门后攻击力将变为x op t。最终drd 受到的伤害为对方初始攻击力x依次经过所有n扇防御门后转变得到的攻击力。
由于atm水平有限,他的初始攻击力只能为0到m之间的一个整数(即他的初始攻击力只能在0,1,…,m中任选,但在通过防御门之后的攻击力不受 m的限制)。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。

输入
第1行包含2个整数,依次为n,m,表示drd有n扇防御门,atm的初始攻击力为0到m之间的整数。接下来n行,依次表示每一扇防御门。每行包括一个字符串op和一个非负整数t,两者由一个空格隔开,且op在前,t在后,op表示该防御门所对应的操作, t表示对应的参数。
n<=10^5 ,0<=m<=10^9 ,0<=t<=10^9 ,op一定为OR,XOR,AND 中的一种

输出
一行一个整数,表示atm的一次攻击最多使 drd 受到多少伤害。

样例输入
3 10
AND 5
OR 6
XOR 7

样例输出
1

思路
or , xor , and运算在二进制下都不会产生进位,所以从高位到低位考虑所选的数x的二进制表示的每一位就行。枚举第i位选0和选1与后面每个参数的第i位运算。
当且仅当满足以下2个条件时,第k位选1:
1、 已经填好的数加上 1 << k 不超过m
2、 选1时结果的第k位为1,选0时结果的第k位为0
因为当选0和选1造成结果一样时,应该优先选择小的数,保证后面选1尽量满足条件一。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
pair<string, int> P[100005];
int n,m;
int cal(int bit, int now){
    for(int i = 0; i < n; i++){
        int x = P[i].second >> bit & 1; //取第i个参数的第bit位
        if(P[i].first == "AND") now &= x;
        else if(P[i].first == "XOR") now ^= x;
        else now |= x;
    }
    return now;
}
int main(){
    cin>>n>>m;
    for(int i = 0; i < n; i++){
        cin>>P[i].first>>P[i].second;
    }
    int val = 0, ans = 0; // val维护所选的数,ans位结果
    for(int bit = 30; bit >= 0; bit--){ // 2^31 == 2,147,483,648
        int res0 = cal(bit,0); // x的bit位选0时,结果的bit位的数值
        int res1 = cal(bit,1); // x的bit位选1时,结果的bit位的数值
        if(val + (1<<bit) <= m && res0 == 0 && res1 == 1){
            val += 1 << bit;
            ans += 1 << bit;
        }
        else ans += res0 << bit;
    }
    cout<<ans<<endl;
}
posted @ 2021-01-03 13:34  CamusJohnson  阅读(244)  评论(1)    收藏  举报