校赛
1. 二分
小数
double l = 0,r = 100000,eps = 1e-4;
while (r-l >= eps){
double mid=(l+r)/2;
if (check(mid)) l=mid;
else r=mid;
}
整数
int l = 1,r = n,mid = (l+r)>>1;
while (l<r){
mid = (l+r+1)>>1;
if (check(mid)) l = mid;
else r = mid-1;
}
2. 并查集
O(logn)
int init(){
for (int i = 1;i <= n;i++) fa[i] = i;
}
int find(int x){
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void update(int x,int y){
int fu = find(x),fv = find(y);
fa[fu] = fv;
}
3. 递推
01背包
for (int i = 1;i <= n;i++) for (int j = m;j >= 0;j--) if (j >= w[i]) dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
完全背包
for (int i = 1;i <= n;i++) for (int j = 0;j <= m;j++) if (j >= w[i]) dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
多重背包
for (int i = 1;i <= n;i++) for (int j = m;j >= 0;j--) for (int k = 1;k <= num[i];k++){
if (j >= w[i]*k) dp[j] = max(dp[j],dp[j-k*w[i]]+k*v[i]);
}
多重背包的优化:把一个物品的不同个数(这里是\(2^n\))看成是不同物品,然后01背包
int totlW = 0;
memset(w,0,sizeof w);
for (int i = 1;i <= n;i++){
for(int j = 1;j <= num[i];j <<= 1){
w[totlW++] = j * weight[i];
num[i] -= j;
}
if(num[i] > 0) w[totlW++] = num[i] * weight[i];
}
4. gcd
O(2log(max(a,b)))
int gcd(int a,int b){
if (!b) return a;
return gcd(b,a % b);
}
5. 最短路
Dij:Dij还可以用来计算多源最短路,只需要将每个起点都放入最初的队列
O(mlogn)
struct node{
int tot = 0;
int head[maxn],to[maxn],nxt[maxn];
long long w[maxn];
void add(int u,int v,int k){
to[++tot] = v,w[tot] = k,nxt[tot] = head[u],head[u] = tot;
}
}ed;
long long dis[maxn];
bool vis[maxn];
priority_queue<pair<long long,int> > q;
void Dij(){
for (int i = 1;i <= n;i++) dis[i] = inf,vis[i] = 0;
dis[s] = 0;
q.push(make_pair(0,s));
while (!q.empty()){
int x = q.top().second;q.pop();
if (vis[x]) continue;
vis[x] = 1;
for (int i = ed.head[x];i;i = ed.nxt[i]){
int to = ed.to[i];
if (dis[to] > dis[x] + ed.w[i]) {
dis[to] = dis[x] + ed.w[i];
q.push(make_pair(-dis[to],to));
}
}
}
}
SPFA:SPFA判负环,如果一个点入队n次则有负环
O(nm)
void SPFA(){
for (int i = 1;i <= n;i++) dis[i] = inf;
dis[s] = 0,vis[s] = 1;
q.push(s);
while (!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for (int i = ed.head[x];i;i = ed.nxt[i]){
int to = ed.to[i],w = ed.w[i];
if (dis[to] > dis[x] + w){
dis[to] = dis[x] + w;
if (!vis[to]){
vis[to] = 1;
q.push(to);
}
}
}
}
}
Floyd
void Floyd(){
for (int k = 1;k <= n;k++)
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++) dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
}
6. 广度优先搜索
找最短路径,如果有特殊限制可能需要优先队列
7. 深度优先搜索
注意:多找剪枝的情况,记忆化搜索
8. 二分图匹配
O(顶点数*边数)
最大匹配:
int dfs(int x){
for (int i = 1;i <= k;i++){
if (!edge[x][i]) continue;
if (vis[i]) continue;
vis[i] = 1;
if (!match[i]||dfs(match[i])){
match[i] = x;
return 1;
}
}
return 0;
}
memset(match,0,sizeof(match));
for (int i = 1;i <= n;i++){
memset(vis,0,sizeof(vis));
if (dfs(i)) ans++;
}
最小点覆盖 = 最大匹配:选取最少的点,使得所有边都至少与一个被选取点相连
最大独立集 = 总点数 - 最大匹配:选取最多的点,使得其中任意两点互不相达
最小边覆盖 = 总点数 - 最大匹配:选取最少的边使得所有的点被至少一条边覆盖
9. 博弈
可以数学推导,也可以打表SG函数算
SG函数:先打表求出一定范围内每个小状态的SG值,发现规律后(发现不了规律可以硬算),总状态的SG值为每个小状态的SG值的异或和,为0就是第一个人输了
map<pair<int,int>,int> mp;
int getSG1(int a,int b){
bool vis[maxn];
if (mp.count(make_pair(a,b))) return mp[make_pair(a,b)];
memset(vis,0,sizeof(vis));
for (int i = 1;i < a;i++) vis[getSG1(i,a-i)] = 1;
for (int i = 1;i < b;i++) vis[getSG1(i,b-i)] = 1;
for (int i = 0;i <= 10000;i++) if (!vis[i]){mp[make_pair(a,b)] = i;break;}
return mp[make_pair(a,b)];
}
10. 一些STL的用法
存储结构体要重载运算符
struct node{
int x = 0,y = 0;
bool operator < (const node &a)const{
if (x != a.x) return x < a.x;
return y < a.y;
}
}fig[maxn];
set 集合:有序不重复/unordered_set:只去重不排序/multiset:有序可重
s.insert();
s.clear();
set<int>::iterator it = s.find();
s.erase(s.find());//删除这个位置
s.erase(value);//删除一个值
s.erase(s.find(),s.find())//删除一个区间
s.size();
s.count();//某个值出现了多少次
set<int>::iterator it_low = st.lower_bound("i");//第一个大于等于
set<int>::iterator it_up = st.upper_bound("i");//第一个大于
for (set<int>::iterator it = s.begin();it != s.end();it++){
printf("%d ",*it);
}
map/unorderd_map/multimap
这里需要注意mulitmap只支持mp.insert(pair<int,string>(1,"ssss"));
if (mp.find(x) != mp.end())
11. 快速幂
int qpow(int x,int k){
int res = 1;
while (k){
if (k&1) (res *= x) %= mod;
(x *= x) %= mod;
k >>= 1;
}
return res % mod;
}
矩阵快速幂
可以用作dp优化,我们通常把dp[i]构造出一个\(n\times1\)的矩阵,转移矩阵为n\(\times\)n,如斐波那契:
struct mul{
long long a[maxn][maxn];
}a,I;
mul operator *(const mul &x,const mul &y){ //重载运算符
mul z;
memset(z.a,0,sizeof(z.a));
for (int k = 1;k <= n;k++)
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
z.a[i][j] = (z.a[i][j] + x.a[i][k]*y.a[k][j] % mod) % mod;
return z;
}
void qpow(long long k){
for (int i = 1;i <= n;i++) for (int j = 1;j <= n;j++) I.a[i][j] = 0;
for (int i = 1; i <= n;i++) I.a[i][i] = 1;
while(k){
if (k&1) I = I * a;
a = a*a;
k >>= 1;
}
}
12. 离散化
sort(que+1,que+1+cnt);
len = unique(que+1,que+cnt+1)-que-1;
for(int i = 1;i <= n;i ++){
s[i] = lower_bound(que + 1, que + len + 1, k[i]) - que;
}