回来了一本算法竞赛进阶指南。可喜可贺,可喜可贺。
于是怀揣着期末考试GG的决心,我….开刷了(x)
顺便一提。。打*的题太丧病了所以就不做了。。
基本概念
bit是度量信息的基本单位。
0x表示16进制,0xff代表-1,0x7f代表128。
在m位二进制中,从右到左最低位为第0位,最高位为第m-1位。
我们常用0x3f3f3f3f表示最大值,因为二倍是int最大整数,每8位相同。
memset语句只能赋值出每8位相同的int,memset(a, 0x3f, sizeof a)
可以用来初始化成最大值。
补码能比反码多表示一个数,减少了特殊判断,在电路设计中很简洁。
1<<n
1
<<
n
表示
2n
2
n
,
n<<1
n
<<
1
表示
n×2
n
×
2
,
n>>1
n
>>
1
表示
⌊n2.0⌋
⌊
n
2.0
⌋
所以我学了啥
位运算优先级:
+ -
>
>
<< >>
> < == !=
>
>
&
^
>
>
|
快速幂与慢速乘
快速幂模板题 POJ1995
#include <iostream>
using namespace std;
typedef long long LL;
LL T, n, m, qwq, tmpa, tmpk, ans;
inline LL ksmqm(LL a, LL k, LL mod) {
LL ans = 1;
for(; k; k >>= 1) {
if(k & 1) ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>T;
while(T--) {
ans = 0;
cin>>m;
cin>>n;
while(n--) {
cin>>tmpa>>tmpk;
ans = (ans + ksmqm(tmpa, tmpk, m)) % m;
}
cout<<ans<<endl;
}
return 0;
}
慢速乘法
法一:对b二进制拆分然后跑一遍快速幂,
法二:玄学的过程?
LL mul(LL a, LL b, LL p){
a %= p, b %= p;
LL c = (long double) a * b - c * p;
LL ans = a * b - c * p;
if(ans < 0) ans += p;
else if(ans >= p) ans -= p;
}
二进制状态压缩
取出第k位 (n >> k) & 1
取出后k位 n & ((1 << k) - 1)
取反第k为 n ^ (1 << k)
第k位赋值1 n | (1 << k)
第k位赋值0 n & (~(1 << k))
例题
例二状压DP。。。
POJ2288打了*先不讨论
Hamilton路径
求从节点0到节点n - 1的最短Hamilton路径。其中Hamilton路径指从1~n恰好不重不漏的经过每个节点一次。 n≤20 n ≤ 20
状态设计:用dp[s][i]表示在节点i处状态为s,s的每一位分别指代某个节点是否访问过。
初始化:dp[s][i] = INF, dp[1][0] = 0表示从起点出发的路一开始长度是0。
转移:dp[s][i] = min{dp[s ^ (1 << j)][k] + w[k][j]},其中k、j已访问过(用i >> j & 1即可判断),表示从j节点没有被访问过的情况转移到这里。
结果:dp[(1 << n ) - 1][n - 1],表示终点处且每个点都访问过的情况。
复杂度
O(n2⋅2n)
O
(
n
2
⋅
2
n
)
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3fffff
int w[26][26];
int dp[1 << 20][20];
int n, m, f, t, va;
int main() {
scanf("%d%d", &n, &m);
memset(w, 0x3f, sizeof w);
for(int i = 1; i <= m; ++i) {
scanf("%d%d%d", &f, &t, &va);
--f; --t;
w[f][t] = va;
w[t][f] = va;
}
memset(dp, 0x3f, sizeof dp);
dp[1][0] = 0;
int M = 1 << n;
for(int i = 1; i < M; ++i) {
for(int j = 0; j < n; ++j)
if(i >> j & 1)
for(int k = 0; k < n; ++k)
if(i >> k & 1)
dp[i][j] = min(dp[i][j], dp[i ^ 1 << j][k] + w[k][j]);
}
cout<<dp[M - 1][n - 1];
return 0;
}
/*
4 5
1 2 6
2 3 7
0 3 5
1 3 1
2 0 2
*/
其它运算
成对变换:
n为偶数,n ^ 1为n + 1
n位奇数,n ^ 1为n - 1
lowbit:
inline int lowbit(int x) {return x & -x;}
用来取出最低位的1及其后面的0构成的数值。
原理好麻烦的样子(x