intstart(int n){ for(int i = 1; i <= n; i ++) pre[i] = i; }
树状数组
用于部分替代线段树的数据结构。
单点查询与单点修改均为 $O(\log n)$
namespace Tree_array{ int tree[N];
intlowbit(int x){ return x & (- x); } voidadd(int x , int val){ for(int i = x; i <= n; i += lowbit(i)){ tree[i] += val; } } intquery(int x){ int ans = 0; for(int i = x; i ; i -= lowbit(i)){ ans += tree[i]; } return ans; } }
另外,树状数组可以用来求逆序对数,时间复杂度也为 $O(\log n)$
#define N 100001
int n , a[N] , b[N];
namespace Tree_array{ int tree[N];
intlowbit(int x){ return x & (-x); }
voidadd(int x , int val){ for(int i = x; i <= n; i += lowbit(i)){ tree[i] += val; } } longlongquery(int x){ longlong ans = 0; for(int i = x; i ; i -= lowbit(i)){ ans += tree[i]; } return ans; } }
namespace Solve{ voidinput(){ cin >> n; for(int i = 1; i <= n; i ++){ cin >> a[i]; b[i] = a[i]; } sort(b + 1 , b + 1 + n); ll number = unique(b + 1 , b + 1 + n) - b - 1; for(int i = 1; i <= number; i ++){ a[i] = lower_bound(b + 1 , b + 1 + n , a[i]) - b; } }
longlongmain(){ longlong ans; input(); for(int i = 1; i <= n; i ++){ Tree_array :: add(a[i] , 1); ans += i - Tree_array :: query(a[i]); } return ans; } }
线段树
用来维护一段序列的数据结构,主要利用了分治思想。
缺陷在于要维护的元素必须具有区间可加性。
建树的时间复杂度为 $O(n)$,其他操作均为 $O(\log n)$
//区间加 & 区间查询
namespace Segment_tree{ #define lson(x) x << 1 #define rson(x) x << 1 | 1
inlinevoidpushdown(int x , int l , int r){ if(!node[x].add_tag) return ; int mid = (l + r) >> 1; node[lson(x)].add_tag += node[x].add_tag; node[lson(x)].num += node[x].add_tag * (mid - l + 1); node[rson(x)].add_tag += node[x].add_tag; node[rson(x)].num += node[x].add_tag * (r - mid); node[x].add_tag = 0; }
voidbuild(int x , int l , int r){ if(l == r){ node[x].num = a[l]; return ; } int mid = (l + r) >> 1; build(lson(x) , l , mid); build(rson(x) , mid + 1 , r); pushup(x); }
voidrange_add(int x , int l , int r , int ql , int qr , int add){ if(ql <= l && r <= qr){ node[x].add_tag += add; node[x].num += add * (r - l + 1); return ; } pushdown(x , l , r); int mid = (l + r) >> 1; if(ql <= mid){ range_add(lson(x) , l , mid , ql , qr , add); } if(mid + 1 <= qr){ range_add(rson(x) , mid + 1 , r , ql , qr , add); } pushup(x); } intrange_query(int x , int l , int r , int ql , int qr){ if(ql <= l && r <= qr){ return node[x].num; } pushdown(x , l , r); int ans = 0; int mid = (l + r) >> 1; if(ql <= mid){ ans += range_query(lson(x) , l , mid , ql , qr); } if(mid + 1 <= qr){ ans += range_query(rson(x) , mid + 1 , r , ql , qr); } return ans; } }
//区间加 & 区间乘 & 区间查询
namespace Segment_tree{ #define lson(x) x << 1 #define rson(x) x << 1 | 1
int a[N] , HA;
structNode{ int num; int add_tag , mul_tag; }node[N << 2];
voidrange_add(int x , int l , int r , int ql , int qr , int val){ if(ql <= l && r <= qr){ node[x].add_tag += val; node[x].add_tag %= HA; node[x].num += val * (r - l + 1); node[x].num %= HA; return ; } pushdown(x , l , r); int mid = (l + r) >> 1; if(ql <= mid){ range_add(lson(x) , l , mid , ql , qr , val); } if(mid + 1 <= qr){ range_add(rson(x) , mid + 1 , r , ql , qr , val); } pushup(x); }
voidrange_mul(int x , int l , int r , int ql , int qr , int val){ if(ql <= l && r <= qr){ node[x].mul_tag *= val; node[x].mul_tag %= HA; node[x].add_tag *= val; node[x].add_tag %= HA; node[x].num *= val; node[x].num %= HA; return ; } int mid = (l + r) >> 1; pushdown(x , l , r); if(ql <= mid){ range_mul(lson(x) , l , mid , ql , qr , val); } if(mid + 1 <= qr){ range_mul(rson(x) , mid + 1 , r , ql , qr ,val); } pushup(x); }
intrange_query(int x , int l , int r , int ql , int qr){ if(ql <= l && r <= qr){ return node[x].num % HA; } pushdown(x , l , r); int ans = 0; int mid = (l + r) >> 1; if(ql <= mid){ ans += range_query(lson(x) , l ,mid , ql , qr); ans %= HA; } if(mid + 1 <= qr){ ans += range_query(rson(x) , mid + 1 , r , ql , qr); ans %= HA; } return ans % HA; }
}
Trie树
字典树,时间复杂度为 $O(|s|)$
namespace Trie{ structNode{ int son[27] , cnt , size; }node[N];
int tot;
voidinsert(string s){ int len = s.length() , rt = 0; for(int i = 0; i < len; i ++){ int c = s[i] - 'a'; if(!node[rt].son[c]){ node[rt].son[c] = ++ tot; } rt = node[rt].son[c]; node[rt].size ++; } node[rt].cnt ++; }
intfind(string s){ int len = s.length() , rt = 0; for(int i = 0; i < len; i ++){ int c = s[i] - 'a'; if(!node[rt].son[c]){ return0; } rt = node[rt].son[c]; } return node[rt].cnt; }
intget(string s){ int len = s.length() , rt = 0; for(int i = 0; i < len; i ++){ int c = s[i] - 'a'; if(!node[rt].son[c]){ return0; } rt = node[rt].son[c]; } return node[rt].size; } }
ODT
即珂朵莉树,原理依托于区间推平操作,无稳定复杂度。但在数据随机的情况下具有良好效果。
namespace ODT{ int n , m , s; int a[N];
structNode { int l , r; mutableint val; booloperator < (const Node &b) const { return l < b.l; } };
set<Node> S;
voidbuild(int n){ for(int i = 1; i <= n; i ++){ S.insert(Node{i , i , a[i]}); } }
set<Node>::iterator spilt(int pos){ set<Node>::iterator it = S.lower_bound((Node){pos , pos , -1}); if(it != S.end() && it -> l == pos){ return it; } it --; Node ins = *it; S.erase(it); S.insert((Node){ins.l , pos - 1 , ins.val}); return S.insert((Node){pos , ins.r , ins.val}).first; }
voidassign(int l , int r , int val){ set<Node>::iterator R = spilt(r + 1); set<Node>::iterator L = spilt(l); S.erase(L , R); S.insert(Node{l , r , val}); }
voidrange_add(int l , int r , int val){ set<Node>::iterator R = spilt(r + 1); set<Node>::iterator L = spilt(l); for(set<Node>::iterator i = L; i != R; i ++){ i -> val += val; } }
intPow(int a , int n , int HA){ if(n == 0) return1; if(n == 1) return a % HA; int c = Pow(a , n / 2 , HA) % HA; if(n % 2 == 0) return (c * c) % HA; else return (((c * c) % HA) * a) % HA; }
intrange_pow(int l , int r , int val , int HA){ set<Node>::iterator R = spilt(r + 1); set<Node>::iterator L = spilt(l); int ans = 0; for(set<Node>::iterator i = L; i != R; i ++){ ans += (int)(i -> r - i -> l + 1) * Pow(i -> val , val , HA); ans %= HA; } return ans; }
intrange_query(int l , int r , int k){ #define pairs pair<int , int>
vector<pairs> V; V.clear(); set<Node>::iterator R = spilt(r + 1); set<Node>::iterator L = spilt(l); for(set<Node>::iterator i = L; i != R; i ++){ V.push_back(pairs(i -> val , i -> r - i -> l + 1)); } sort(V.begin() , V.end()); int sum = 0; for(vector<pairs> :: iterator i = V.begin(); i != V.end(); i ++){ sum += i -> second; if(sum >= k){ return i -> first; } } return-1;
voidspilt(int n , int k , int &x , int &y){ if(!n) x = y = 0; elseif(node[n].dis >= k){ spilt(lson(n) , k , x , y); lson(n) = y; update(y = n); } else{ spilt(rson(n) , k , x , y); rson(n) = x; update(x = n); } }
dis[sta] = 0; q.push(sta); vis[sta] = 1; while(!q.empty()){ int fro = q.front(); q.pop(); vis[fro] = false; for(int i = hend[fro]; i ; i = edge[i].last){ int to = edge[i].to; if(dis[to] > dis[fro] + edge[i].dis){ dis[to] = dis[fro] + edge[i].dis; if(!vis[to]){ vis[to] = 1; q.push(edge[i].to); } } } } }
Kruskal
最小生成树算法,时间复杂度为 $O(n \log n)$
intKruskal(){ for(int i = 1; i <= n; i ++){ pre[i] = i; } int num , ans; sort(edge + 1 , edge + 1 + m , cmp); for(int i = 1; i <= m; i ++){ int l = find(edge[i].from); r = find(edge[i].to); if(l == r) continue; ans += edge[i].dis; pre[l] = r; num ++; if(num == n - 1) break; } return ans; }
Tarjan(割点)
使用 $\text{Tarjan}$ 算法求割点,时间复杂度为 $O(n)$
int dfn[N] , low[N] , cnt; bool cut[N];
inlinevoidTarjan(int x , int fa){ dfn[x] = low[x] = ++ cnt; int child = 0; for(int i = hend[x]; i ; i = edge[i].last){ int to = edge[i].to; if(!dfn[to]){ Tarjan(to , fa); low[x] = min(low[x] , low[to]); if(low[to] > dfn[x] && x != fa) cut[x] = 1; } if(x == fa) child ++; low[x] = min(low[x] , dfn[to]); } if(child >= 2 && x == fa) cut[x] = true; }
LCA
倍增求最近公共祖先。
预处理为 $O(n \log n)$,每一次查询为 $O(\log n)$
namespace LCA{ int fa[N][22]; int dep[N];
voidsearch(int x){ for(int i = head[x]; i ; i = edge[i].last){ int to = edge[i].to; if(to == fa[x][0]) continue; fa[to][0] = x; dep[to] = dep[x] + 1; search(to); } }
voidbuild(){ for(int j = 1; j <= log2(n); j ++){ for(int i = 1; i <= n; i ++){ fa[i][j] = fa[fa[i][j - 1]][j - 1]; } } }
intLCA(int x , int y){ if(dep[x] < dep[y]) swap(x , y); while(dep[x] > dep[y]){ x = fa[x][(int )log2(dep[x] - dep[y])]; } if(x == y) return x; for(int i = log2(dep[x]); i >= 0; i --){ if(dep[x] != dep[y]){ x = fa[x][i]; y = fa[y][i]; } } return fa[x][0]; } }
Others
竞赛模板
作者常用的考场代码模板,采用宏定义的方式定义了一些常见的代码写法,熟练使用可以大幅度提高编码速度。
#include<bits/stdc++.h>
#define I inline #define fi first #define se second #define pb push_back #define MP make_pair #define PII pair<int , int> #define PSL pair<string , long long> #define PLL pair<long long , long long> #define all(x) (x).begin() , (x).end() #define copy(a , b) memcpy(a , b , sizeof(b)) #define clean(a , b) memset(a , b , sizeof(a)) #define rep(i , l , r) for (int i = (l); i <= (r); i ++) #define per(i , r , l) for (int i = (r); i >= (l); i --) #define PE(i , x) for(int i = head[x]; i; i = edge[i].last) #define DEBUG(x) std :: cerr << #x << '=' << x << std :: endl
typedeflonglong ll; typedefunsignedlonglong ull;
template <classT> inlinevoidread(T &x){ char c = getchar(), f = 0; x = 0; while (!isdigit(c)) f = (c == '-'), c = getchar(); while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x = f ? -x : x; }
usingnamespacestd;
constint N = 10001; constint M = 100001; constint HA = 998244353;
template <classT> inlinevoidread(T &x){ char c = getchar(), f = 0; x = 0; while (!isdigit(c)) f = (c == '-'), c = getchar(); while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x = f ? -x : x; }
intmain(){ while(1){ system("cls"); do{ num ++; system("data.exe"); s = clock(); system("a.exe"); t = clock(); system("b.exe"); if(system("fc try1.out try2.out")) break; else{ printf("AC on text #%d , time is %d\n" , num , t - s); } }while(true); printf("WA on text #%d , time is %d\n" , num , t - s); system("fc try1.out try2.out"); system("pause > nul"); } return0; }