模板 2.0
备忘录:
-
线段树模板查询query和更新update操作初始化l, r, root默认值
-
Z函数、 KMP求循环节
关闭同步输入输出流
ios::sync_with_stdio(false)
控制小数位数
#include <iomanip>
cout<<fixed<<setprecision(6)<<res<<endl;
局部排序函数
partial_sort(a + 1, a + 1 + 20, a + n + 1, greater<int>());
partial_sort(a.begin(), a.begin() + 20, a.end(), greater<int>());
归并排序(求逆序对个数)
int a[N], tmp[N];
ll mergesort(int p[], int l, int r)
{
if(l >= r) return 0;
int mid = l + r >> 1;
ll res = 0;
res += mergesort(p, l, mid) + mergesort(p, mid + 1, r);
int k = 1, i = l, j = mid + 1;
while(i <= mid && j <= r)
{
if(p[i] <= p[j]) tmp[k ++] = p[i ++];
else tmp[k ++] = p[j ++], res += mid - i + 1;
}
while(i <= mid) tmp[k ++] = p[i ++];
while(j <= r) tmp[k ++] = p[j ++];
for(int i = l, j = 1;i <= r;i ++, j ++) p[i] = tmp[j];
return res;
}
快速排序
int a[N];
void quicksort(int p[], int l, int r)
{
if(l >= r) return;
int x = p[l + r >> 1], i = l - 1, j = r + 1;
while(i < j)
{
do i ++; while(p[i] < x);
do j --; while(p[j] > x);
if(i < j) swap(p[i], p[j]);
}
quicksort(p, l, j);
quicksort(p, j + 1, r);
}
高精度加法
vector<int> add(vector<int> &A, vector<int> &B)
{
vector<int>C;
int t = 0;
for(int i = 0;i < A.size() || i < B.size();i ++)
{
if(i < A.size()) t += A[i];
if(i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if(t) C.push_back(t);
return C;
}
int main()
{
string a, b; cin>>a>>b;
vector<int> A, B;
for(int i = a.length() - 1;i >= 0;i --) A.push_back(a[i] - '0');
for(int i = b.length() - 1;i >= 0;i --) B.push_back(b[i] - '0');
auto C = add(A, B);
for(int i = C.size() - 1;i >= 0;i --) cout<<C[i]; cout<<endl;
return 0;
}
高精度减法
vector<int> cub(vector<int> &A, vector<int> &B)
{
vector<int> C;
int t = 0;
for(int i = 0;i < A.size() || i < B.size();i ++)
{
if(i < A.size()) t += A[i];
if(i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if(t < 0) t = -1;
else t = 0;
}
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
string a, b; cin>>a>>b;
vector<int> A, B;
for(int i = a.length() - 1;i >= 0;i --) A.push_back(a[i] - '0');
for(int i = b.length() - 1;i >= 0;i --) B.push_back(b[i] - '0');
if(a.length() < b.length() || (a.length() == b.length() && a < b))
{
auto C = cub(B, A);
cout<<"-";
for(int i = C.size() - 1;i >= 0;i --) cout<<C[i]; cout<<endl;
}
else
{
auto C = cub(A, B);
for(int i = C.size() - 1;i >= 0;i --) cout<<C[i]; cout<<endl;
}
return 0;
}
高精度乘法
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
for(int i = 0;i < A.size() || t;i ++)
{
if(i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
string a;
int b;
cin>>a>>b;
vector<int>A;
for(int i = a.length() - 1;i >= 0;i --) A.push_back(a[i] - '0');
auto C = mul(A, b);
for(int i = C.size() - 1;i >= 0;i --) cout<<C[i];
cout<<endl;
return 0;
}
高精度除法
vector<int> div(vector<int> &A, int b, int &r)
{
vector<int> C;
r = 0;
for(int i = A.size() - 1;i >= 0;i --)
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
string a;
int b, r;
cin>>a>>b;
vector<int>A;
for(int i = a.length() - 1;i >= 0;i --) A.push_back(a[i] - '0');
auto C = div(A, b, r);
for(int i = C.size() - 1;i >= 0;i --) cout<<C[i];
cout<<endl<<r<<endl;
return 0;
}
二维差分
int a[N][N], pre[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
pre[x1][y1] += c;
pre[x2 + 1][y1] -= c;
pre[x1][y2 + 1] -= c;
pre[x2 + 1][y2 + 1] += c;
}
int main()
{
int n, m, q; cin>>n>>m>>q;
for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) cin>>a[i][j];
for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++) insert(i, j, i, j, a[i][j]);
for(int i = 1;i <= q;i ++)
{
int x1, y1, x2, y2, c; cin>>x1>>y1>>x2>>y2>>c;
insert(x1, y1, x2, y2, c);
}
for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++)
pre[i][j] += pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1];
for(int i = 1;i <= n;i ++) for(int j = 1;j <= m;j ++)
cout<<pre[i][j]<<(j == m ? '\n' : ' ');
return 0;
}
离散化
int find(int x) {return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;}
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
KMP
int nx[N];
int main()
{
int n, m;
string p, s;
cin>>n>>p>>m>>s;
p = '_' + p; s = '_' + s;
for(int i = 2, j = 0;i <= n;i++)
{
while(j && p[i] != p[j + 1]) j = nx[j];
if(p[i] == p[j + 1]) j ++;
nx[i] = j;
}
for(int i = 1, j = 0;i <= m;i ++)
{
while(j && s[i] != p[j + 1]) j = nx[j];
if(s[i] == p[j + 1]) j ++;
if(j == n) cout<<i - n<<" ", j = nx[j];
}
return 0;
}
扩展KMP
char a[N], b[N];
int z[N], p[N];
// b 的 Z函数,即 b 与 b 的每一个后缀的 LCP 长度。
inline void Z(char *s, int n)
{
for(int i = 1;i <= n;i ++) z[i] = 0;
z[1] = n;
for(int i = 2, l = 0, r = 0;i <= n;i ++)
{
if(i <= r) z[i] = min(z[i - l + 1], r - i + 1);
while(i + z[i] <= n && s[i + z[i]] == s[z[i] + 1]) ++ z[i];
if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
}
// b 与 a 的每一个后缀的 LCP 长度。
inline void exkmp(char *s, int n, char *t, int m)
{
Z(t, m);
for(int i = 1;i <= n;i ++) p[i] = 0;
for(int i = 1, l = 0, r = 0;i <= n;i ++)
{
if(i <= r) p[i] = min(z[i - l + 1], r - i + 1);
while(i + p[i] <= n && s[i + p[i]] == t[p[i] + 1]) ++ p[i];
if(i + p[i] - 1 > r) l = i, r = i + p[i] - 1;
}
}
main()
{
scanf("%s%s", a + 1, b + 1);
int n = strlen(a + 1), m = strlen(b + 1);
exkmp(a, n, b, m);
}
字典树
int son[N][26], cnt[N], idx;
void insert(string s)
{
int p = 0;
for(auto &c : s)
{
if(!son[p][c - 'a']) son[p][c - 'a'] = ++ idx;
p = son[p][c - 'a'];
}
cnt[p] ++;
}
int query(string s)
{
int p = 0;
for(auto &c : s)
{
if(!son[p][c - 'a']) return 0;
p = son[p][c - 'a'];
}
return cnt[p];
}
并查集
int parent[N];
int find_root(int x)
{
if(parent[x] != x) parent[x] = find_root(parent[x]);
return parent[x];
}
//初始化
for(int i = 1;i <= n;i ++) parent[i] = i;
//合并
parent[find_root(a)] = find_root(b);
模拟堆
int heap[N], idx;
void push_up(int x)
{
while(x / 2 && heap[x / 2] > heap[x])
{
swap(x, x / 2);
x >>= 1;
}
}
void push_down(int x)
{
int t = x;
if(x * 2 <= idx && heap[x * 2] < heap[t]) t = x * 2;
if(x * 2 + 1 <= idx && heap[x * 2 + 1] < heap[t]) t = x * 2 + 1;
if(t != x)
{
swap(t, x);
push_down(t);
}
}
字符串双重哈希
class HASH
{
vector<ull> hs, pn;
int n, p = 131;
public:
HASH(string s)
{
n = s.length();
hs.resize(n + 1);
pn.resize(n + 1);
pn[0] = 1;
for(int i = 1;i <= n;i ++)
{
hs[i] = hs[i - 1] * p + s[i - 1];
pn[i] = pn[i - 1] * p;
}
}
// 获得l - r区间上字符串的哈希值
int gethash(int l, int r)
{
if(l > r) return 0ll;
return hs[r] - hs[l - 1] * pn[r - l + 1];
}
};
树的重心
int n, ans = 1e5 + 10;
bool vis[N];
vector<int> e[N];
int dfs(int u)
{
vis[u] = true;
int sum = 1, mx = 0;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i];
if(!vis[v])
{
int temp = dfs(v);
mx = max(mx, temp);
sum += temp;
}
}
mx = max(mx, n - sum);
ans = min(ans, mx);
return sum;
}
拓扑排序
int n, m, indeg[N], ans[N], cnt;
vector<int> e[N];
bool topsort()
{
queue<int> que;
for(int i = 1;i <= n;i ++) if(!indeg[i]) que.push(i);
while(!que.empty())
{
int u = que.front(); que.pop();
ans[++ cnt] = u;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i];
indeg[v] --;
if(!indeg[v]) que.push(v);
}
}
return cnt == n;
}
朴素dijkstra
const int inf = 0x3f3f3f3f;
int a[N][N], dis[N], n, m;
bool vis[N];
int dijkstra()
{
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
dis[1] = 0;
for(int i = 1;i <= n;i ++)
{
int idx = 0;
for(int j = 1;j <= n;j ++) if(!vis[j] && dis[j] <= dis[idx]) idx = j;
vis[idx] = true;
for(int j = 1;j <= n;j ++) if(dis[idx] + a[idx][j] < dis[j]) dis[j] = dis[idx] + a[idx][j];
}
return dis[n];
}
堆优化dijkstra
int n, m, dis[N];
bool vis[N];
vector<pair<int, int>> e[N];
int dijkstra(int start)
{
memset(dis, 0x3f, sizeof dis);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap;
dis[start] = 0;
heap.push({0, start});
while(!heap.empty())
{
int u = heap.top().second; heap.pop();
if(vis[u]) continue;
vis[u] = true;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].first, wi = e[u][i].second;
if(dis[u] + wi < dis[v]) dis[v] = dis[u] + wi, heap.push({dis[v], v});
}
}
return dis[n];
}
bellman-ford
const int inf = 0x3f3f3f3f;
struct node
{
int u, v, w;
}edge[M];
int n, m, k, dis[N], updis[N];
int bellman_ford()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for(int i = 1;i <= k;i ++)
{
memcpy(updis, dis, sizeof dis);
for(int j = 1;j <= m;j ++)
if(updis[edge[j].u] + edge[j].w < dis[edge[j].v])
dis[edge[j].v] = updis[edge[j].u] + edge[j].w;
}
return dis[n];
}
SPFA最短路
int dis[N], n, m;
bool vis[N];
vector<pair<int, int>> e[N];
int spfa(int start)
{
memset(dis, 0x3f, sizeof dis);
queue<int> que;
que.push(start);
dis[start] = 0;
while(!que.empty())
{
int u = que.front(); que.pop();
vis[u] = false;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].first, wi = e[u][i].second;
if(dis[u] + wi < dis[v])
{
dis[v] = dis[u] + wi;
if(!vis[v]) que.push(v), vis[v] = true;
}
}
}
return dis[n];
}
SPFA判负环
int dis[N], cnt[N], n, m;
bool vis[N];
vector<pair<int, int>> e[N];
bool spfa()
{
queue<int> que;
for(int i = 1;i <= n;i ++) que.push(i), vis[i] = true;
while(!que.empty())
{
int u = que.front(); que.pop();
vis[u] = false;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].first, wi = e[u][i].second;
if(dis[u] + wi < dis[v])
{
dis[v] = dis[u] + wi;
cnt[v] = cnt[u] + 1;
if(cnt[v] >= n) return true;
if(!vis[v]) que.push(v), vis[v] = true;
}
}
}
return false;
}
Floyd最短路
const int inf = 0x3f3f3f3f;
int dis[N][N], n, m, k;
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]);
}
朴素prim最小生成树
const int inf = 0x3f3f3f3f;
int e[N][N], dis[N], n, m;
bool vis[N];
int prim()
{
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
int res = 0;
for(int i = 1;i <= n;i ++)
{
int idx = 0;
for(int j = 1;j <= n;j ++) if(!vis[j] && dis[j] < dis[idx]) idx = j;
if(idx == 0) return -1;
vis[idx] = true;
res += dis[idx];
for(int j = 1;j <= n;j ++) if(e[idx][j] < dis[j]) dis[j] = e[idx][j];
}
return res;
}
堆优化prim最小生成树
int dis[N], n, m;
bool vis[N];
vector<pair<int, int>> e[N];
int prim()
{
memset(dis, 0x3f, sizeof dis);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap;
dis[1] = 0;
heap.push({0, 1});
int res = 0, cnt = 0;
while(!heap.empty())
{
int u = heap.top().second; heap.pop();
if(vis[u]) continue;
vis[u] = true;
res += dis[u];
cnt ++;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].first, wi = e[u][i].second;
if(!vis[v] && wi < dis[v])
{
dis[v] = wi;
heap.push({dis[v], v});
}
}
}
if(cnt < n) return -1;
return res;
}
kruskal 最小生成树
int parent[N];
struct node
{
int u, v, w;
bool operator< (const node &y)const{
return w < y.w;
}
}e[M];
int find(int x)
{
if(x != parent[x]) parent[x] = find(parent[x]);
return parent[x];
}
int main()
{
int n, m; cin>>n>>m;
for(int i = 1;i <= n;i ++) parent[i] = i;
for(int i = 1;i <= m;i ++) cin>>e[i].u>>e[i].v>>e[i].w;
sort(e + 1, e + m + 1);
int res = 0, cnt = 0;
for(int i = 1;i <= m;i ++)
{
int u = e[i].u, v = e[i].v, w = e[i].w;
if(find(u) == find(v)) continue;
parent[find(u)] = find(v);
res += w;
cnt ++;
}
if(cnt == n - 1) cout<<res<<endl;
else cout<<"impossible"<<endl;
return 0;
}
二分图判断
int head[N], e[M], nx[M], idx = 1;
int col[N], n, m;
void add(int u, int v)
{
e[idx] = v;
nx[idx] = head[u];
head[u] = idx ++;
}
bool dfs(int u, int c)
{
col[u] = c;
for(int i = head[u];~i;i = nx[i])
{
int v = e[i];
if(!col[v]) dfs(v, 3 - c);
if(col[u] == col[v]) return false;
}
return true;
}
int main()
{
memset(head, -1, sizeof head);
cin>>n>>m;
for(int i = 1;i <= m;i ++)
{
int u, v; cin>>u>>v;
add(u, v); add(v, u);
}
bool flag = true;
for(int i = 1;i <= n;i ++)
{
if(!col[i] && !dfs(i, 1))
{
flag = false;
break;
}
}
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
匈牙利算法
int head[N], e[M], nx[M], idx = 1;
int n, ns, m, match[N];
bool vis[N];
void add(int u, int v)
{
e[idx] = v;
nx[idx] = head[u];
head[u] = idx ++;
}
bool find(int u)
{
for(int i = head[u];~i;i = nx[i])
{
int v = e[i];
if(!vis[v])
{
vis[v] = true;
if(match[v] == 0 || find(match[v]))
{
match[v] = u;
return true;
}
}
}
return false;
}
int query()
{
int res = 0;
for(int i = 1;i <= n;i ++)
{
memset(vis, 0, sizeof vis);
if(find(i)) res ++;
}
return res;
}
int main()
{
memset(head, -1, sizeof head);
cin>>n>>ns>>m;
for(int i = 1;i <= m;i ++)
{
int u, v; cin>>u>>v;
add(u, v);
}
cout<<query()<<endl;
return 0;
}
欧拉路径 点序列(有向图-vector)
int del[N];
int deg[N][2];
stack<int> st;
vector<int> e[N];
void dfs(int now)
{
for(int i = del[now];i < e[now].size();i = del[now])
{
del[now] = i + 1;
dfs(e[now][i]);
}
st.push(now);
}
void solve()
{
int n, m; cin>>n>>m;
for(int i = 1;i <= m;i ++)
{
int u, v; cin>>u>>v;
e[u].push_back(v);
deg[u][1] ++; deg[v][0] ++;
}
for(int i = 1;i <= n;i ++) sort(e[i].begin(), e[i].end());
// 有向图欧拉路径判断
int start = 1, cnt[2] = {0};
bool flag = true;
for(int i = 1;i <= n;i ++)
{
if(deg[i][1] != deg[i][0]) flag = false;
if(deg[i][1] - deg[i][0] == 1) cnt[1] ++, start = i;
if(deg[i][0] - deg[i][1] == 1) cnt[0] ++;
}
if(!flag && !(cnt[0] == cnt[1] && cnt[0] == 1)) {cout<<"No"<<endl; return;}
dfs(start);
while(!st.empty()) cout<<st.top()<<" ", st.pop();
cout<<endl;
}
欧拉回路 边序列(有向图链式前向星)
int h[N], e[M], ne[M], idx;
int deg[N][2];
bool used[M];
stack<int> st;
void add(int u, int v)
{
e[idx] = v, ne[idx] = h[u], h[u] = idx ++;
}
void dfs(int now)
{
for(int &i = h[now];~i;)
{
if(used[i]) {i = ne[i]; continue;}
used[i] = true;
int temp = i + 1;
int j = e[i];
i = ne[i];
dfs(j);
st.push(temp);
}
}
signed main()
{
int n, m; cin>>n>>m;
for(int i = 1;i <= n;i ++) h[i] = -1;
for(int i = 1;i <= m;i ++)
{
int u, v; cin>>u>>v;
add(u, v);
deg[u][1] ++;
deg[v][0] ++;
}
// 有向图欧拉回路判断
for(int i = 1;i <= n;i ++)
if(deg[i][0] != deg[i][1]) {cout<<"NO"<<endl; return 0;}
for(int i = 1;i <= n;i ++)
if(h[i] != -1) {dfs(i); break;}
if(st.size() < m) cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
while(!st.empty()) cout<<st.top()<<" ", st.pop();
cout<<endl;
}
return 0;
}
欧拉回路 边序列(无向图链式前向星)
int h[N], e[M], ne[M], idx;
int deg[N][2];
bool used[M];
stack<int> st;
void add(int u, int v)
{
e[idx] = v, ne[idx] = h[u], h[u] = idx ++;
}
void dfs(int now)
{
for(int &i = h[now];~i;)
{
if(used[i]) {i = ne[i]; continue;}
used[i] = true; used[i ^ 1] = true;
int temp;
temp = i / 2 + 1;
if(i & 1) temp = -temp;
int j = e[i];
i = ne[i];
dfs(j);
st.push(temp);
}
}
signed main()
{
int n, m; cin>>n>>m;
for(int i = 1;i <= n;i ++) h[i] = -1;
for(int i = 1;i <= m;i ++)
{
int u, v; cin>>u>>v;
add(u, v); add(v, u);
deg[u][1] ++; deg[v][0] ++;
}
// 无向图欧拉回路判断
for(int i = 1;i <= n;i ++)
if((deg[i][0] + deg[i][1]) % 2 == 1) {cout<<"NO"<<endl; return 0;}
for(int i = 1;i <= n;i ++)
if(h[i] != -1) {dfs(i); break;}
if(st.size() < m) cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
while(!st.empty()) cout<<st.top()<<" ", st.pop();
cout<<endl;
}
return 0;
}
欧拉筛(线性求欧拉函数)
int prm[N], pre[N], phi[N], cnt;
void get_prm()
{
for(int i = 2;i < N;i ++)
{
if(!pre[i])
{
pre[i] = prm[++ cnt] = i;
// phi[i] = i - 1;
}
for(int j = 1;i * prm[j] < N;j ++)
{
pre[i * prm[j]] = prm[j];
if(i % prm[j] == 0)
{
// phi[i * prm[j]] = phi[i] * prm[j];
break;
}
// phi[i * prm[j]] = phi[i] * (prm[j] - 1);
}
}
}
[L, R]区间质数筛
// 先将[1, sqrt(r)] 内的质数通过欧拉筛预处理
// 通过埃氏筛对[L, R] 内的质数进行筛选
// 区间长度不超过1e6
int l, r; cin>>l>>r;
for(int i = 1;i <= cnt && prm[i] <= r;i ++)
{
int w = (l + prm[i] - 1) / prm[i];
for(int j = max(2ll, w);prm[i] * j <= r;j ++)
vis[prm[i] * j - l] = true;
}
vector<int> prime;
for(int i = l;i <= r;i ++)
{
if(!vis[i - l]) prime.push_back(i);
vis[i - l] = false;
}
GCD
int gcd(int x, int y)
{
if(x % y == 0) return y;
return gcd(y, x % y);
}
扩展欧几里得
// 求 xa + by = gcd(a, b) 的一组解
// 最小正整数解 : (x % (b / gcd) + (b / gcd)) % (b / gcd)
int edgcd(int a, int b, int &x, int &y)
{
if(a % b == 0)
{
x = 0; y = 1;
return b;
}
int d = edgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
线性求逆元
inv[1] = 1;
for(int i = 2;i <= n;i ++) inv[i] = (p - p / i) * inv[p % i] % p;
中国剩余定理
int edgcd(int a, int b, int &x, int &y)
{
if(a % b == 0)
{
x = 0; y = 1;
return b;
}
int d = edgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
signed main()
{
int n; cin>>n;
int a1, m1; cin>>m1>>a1;
bool flag = true;
for(int i = 2;i <= n;i ++)
{
int a2, m2; cin>>m2>>a2;
int k1, k2;
int d = edgcd(m1, -m2, k1, k2);
if((a2 - a1) % d != 0)
{
flag = false;
break;
}
int x = m2 / d;
k1 = k1 * (a2 - a1) / d;
k1 = (k1 % x + x) % x;
int m = abs(m1 / d * m2);
a1 = k1 * m1 + a1;
m1 = m;
}
if(flag) cout<<(a1 % m1 + m1) % m1<<endl;
else cout<<-1<<endl;
return 0;
}
高斯消元求解线性方程组
double mt[N][N];
int gauss(int n, int m) // n 个不等式, m 个未知数, 增广矩阵大小 n * (m + 1)
{
int col, row;
for(col = 1, row = 1;col <= m;col ++)
{
int t = row;
for(int i = row;i <= n;i ++) if(mt[i][col] > mt[t][col]) t = i;
if(fabs(mt[t][col]) < eps) continue;
for(int i = col;i <= m + 1;i ++) swap(mt[row][i], mt[t][i]);
for(int i = m + 1;i >= col;i --) mt[row][i] /= mt[row][col];
for(int i = row + 1;i <= n;i ++) for(int j = m + 1;j >= col;j --)
mt[i][j] -= mt[row][j] * mt[i][col] / mt[row][col];
row ++;
}
if(row <= n)
{
for(int i = row;i <= n;i ++) if(fabs(mt[i][m + 1]) > eps) return 0; //无解
return 1; //无穷多解
}
for(int i = n - 1;i >= 1;i --) for(int j = i + 1;j <= m;j ++)
mt[i][m + 1] -= mt[n - m + j][m + 1] * mt[i][j];
return 2; //有唯一解
}
1000 * 1000 求组合数
int c[N][N];
void init()
{
for(int i = 0;i < N;i ++) for(int j = 0;j <= i;j ++)
{
if(!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
费马小定理 快速幂求组合数
const int mod = 1e9 + 7;
int fact[N], infact[N];
int quickpow(int x, int k)
{
int res = 1;
while(k)
{
if(k & 1) res = res * x % mod;
k >>= 1;
x = x * x % mod;
}
return res;
}
void init()
{
fact[0] = infact[0] =1;
for(int i = 1;i < N;i ++)
{
fact[i] = fact[i - 1] * i % mod;
infact[i] = quickpow(fact[i], mod - 2);
}
}
卢卡斯定理 求组合数 取模的数小基数大时使用
int quickpow(int x, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = res * x % p;
k >>= 1;
x = x * x % p;
}
return res;
}
int C(int a, int b, int p)
{
int c1 = 1, c2 = 1;
for(int i = 1, j = a;i <= b;i ++, j --) c1 = c1 * j % p, c2 = c2 * i % p;
return c1 * quickpow(c2, p - 2, p) % p;
}
int lucas(int a, int b, int p)
{
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
最长上升子序列
int a[N], f[N];
int main()
{
int n; cin>>n;
for(int i = 1;i <= n;i ++) cin>>a[i];
int len = 1;
f[1] = a[1];
for(int i = 2;i <= n;i ++)
{
int temp = lower_bound(f + 1, f + len + 1, a[i]) - f;
len = max(len, temp);
f[temp] = a[i];
}
cout<<len<<endl;
return 0;
}
多重背包 转化成 01背包
int f[N], v[M], w[M];
int main()
{
int n, m; cin>>n>>m;
int cnt = 0;
for(int i = 1;i <= n;i ++)
{
int a, b, s; cin>>a>>b>>s;
int k = 1;
while(k <= s)
{
v[++ cnt] = k * a;
w[cnt] = k * b;
s -= k;
k <<= 1;
}
if(s)
{
v[++ cnt] = s * a;
w[cnt] = s * b;
}
}
for(int i = 1;i <= cnt;i ++) for(int j = m;j >= 0;j --)
if(j >= v[i]) f[j] = max(f[j], f[j - v[i]] + w[i]);
cout<<f[m]<<endl;
return 0;
}
Stirling 阶乘位数
int get_len(int n)
{
int len = 1;
if (n > 3)
len = log10(2 * PI * n) / 2 + n * log10(n / E) + 1;
return len;
}
2的n次幂位数
n*log10(2) + 1;
ST表(查询区间最大值)
int f[N][21], log2n[N];
int main()
{
int n = read(), m = read();
for(int i = 1;i <= n;i++) f[i][0] = read();
log2n[1] = 0; log2n[2] = 1;
for(int i = 3;i <= n;i++) log2n[i] = log2n[i / 2] + 1;
for(int j = 1;j <= 20;j++) for(int i = 1;i + (1 << j) - 1 <= n;i++)
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
for(int i = 1;i <= m;i++)
{
int l = read(), r = read();
int k = log2n[r - l + 1];
printf("%d\n", max(f[l][k], f[r - (1 << k) + 1][k]));
}
return 0;
}
Zeller 求星期几
int Zeller(int year,int month,int date)
{
if(month == 1||month == 2) year --, month += 12;
int c = year / 100;
year -= c * 100;
int week = year + year / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + date-1;
while(week < 0) week += 7;
week %= 7;
return week;
}
Zeller 日期 <----> id
int getid(int y, int m, int d) // 年月日 -> id
{
if(m < 3) {y--; m += 12;}
return 365 * y + y / 4 - y / 100 + y / 400 + (153 * (m - 3) + 2) / 5 + d - 307;
}
vector<int> date(int id) // id -> 年月日
{
int x = id + 1789995, n, i, j, y, m, d;
n = 4 * x / 146097;
x -= (146097 * n + 3) / 4;
i = (4000 * (x + 1)) / 1461001; x -= 1461 * i / 4 - 31;
j = 80 * x / 2447; d = x - 2447 * j / 80; x = j / 11;
m = j + 2 - 12 * x; y = 100 * (n - 49) + i + x;
return vector<int>({y, m, d});
}
加权并查集
int parent[N], val[N];
void initialise()
{
memset(parent, -1, sizeof(parent));
memset(val, 0, sizeof(val));
}
int find_root(int x)
{
if (parent[x] == -1)
return x;
int t = parent[x];
parent[x] = find_root(parent[x]);
val[x] += val[t];
return parent[x];
}
bool union_root(int x, int y, int v)
{
int x_root = find_root(x);
int y_root = find_root(y);
if (x_root == y_root)
return false;
parent[x_root] = y_root;
val[x_root] = val[y] - val[x] + v;
return true;
}
区间赋值线段树
// 方便扩展其他功能,因此保留结点定义
// 建树时将所有lazy标记赋值成 操作中不可能被赋值的值 默认为 -1
struct node
{
int lazy = -1;
};
node tree[N << 2];
int a[N];
void pushdown(int root)
{
if (tree[root].lazy != -1)
{
tree[root << 1].lazy = tree[root].lazy;
tree[root << 1 | 1].lazy = tree[root].lazy;
tree[root].lazy = -1;
}
}
void buildtree(int l, int r, int root)
{
tree[root].lazy = -1;
if (l == r)
{
tree[root].lazy = a[l];
return;
}
int m = l + r >> 1;
buildtree(l, m, root << 1);
buildtree(m + 1, r, root << 1 | 1);
}
// 将[L, R]区间上的数赋值为num
void modify(int l, int r, int L, int R, int root, int num)
{
if (l == L && r == R)
{
tree[root].lazy = num;
return;
}
if (l == r) return;
pushdown(root);
int m = l + r >> 1;
if (R <= m)
modify(l, m, L, R, root << 1, num);
else if (L > m)
modify(m + 1, r, L, R, root << 1 | 1, num);
else
{
modify(l, m, L, m, root << 1, num);
modify(m + 1, r, m + 1, R, root << 1 | 1, num);
}
}
// 查询下标为pos的值
int query(int l, int r, int root, int pos)
{
if (l == r) return tree[root].lazy;
pushdown(root);
int m = l + r >> 1;
if(pos <= m) return query(l, m, root << 1, pos);
return query(m + 1, r, root << 1 | 1, pos);
}
区间最小值线段树
struct node {int mn = 0, lazy = 0;};
node tree[N << 2];
int a[N];
void pushup(int root)
{
tree[root].mn = min(tree[root << 1].mn, tree[root << 1 | 1].mn);
}
void pushdown(int root, int left, int right)
{
int mid = left + right >> 1;
if (tree[root].lazy)
{
tree[root << 1].lazy += tree[root].lazy;
tree[root << 1].mn += tree[root].lazy;
tree[root << 1 | 1].lazy += tree[root].lazy;
tree[root << 1 | 1].mn += tree[root].lazy;
tree[root].lazy = 0;
}
}
void buildtree(int l, int r, int root)
{
tree[root].lazy = 0;
if (l == r)
{
tree[root].mn = a[l];
return;
}
int m = l + r >> 1;
buildtree(l, m, root << 1);
buildtree(m + 1, r, root << 1 | 1);
pushup(root);
}
// [L, R] all add num
void updata(int l, int r, int L, int R, int root, int num)
{
if (l >= L && r <= R)
{
tree[root].lazy += num;
tree[root].mn += num;
return;
}
pushdown(root, l, r);
int m = l + r >> 1;
if(L <= m) updata(l, m, L, R, root << 1, num);
if(R > m) updata(m + 1, r, L, R, root << 1 | 1, num);
pushup(root);
}
// query the min-number of [L, R]
int query(int l, int r, int L, int R, int root)
{
if (l >= L && r <= R) return tree[root].mn;
pushdown(root, l, r);
int m = l + r >> 1;
int res = inf;
if(L <= m) res = min(res, query(l, m, L, R, root << 1));
if(R > m) res = min(res, query(m + 1, r, L, R, root << 1 | 1));
pushup(root);
return res;
}
区间最大值线段树
struct node {int mx = 0, lazy = 0;};
node tree[N << 2];
int a[N];
void pushup(int root)
{
tree[root].mx = max(tree[root << 1].mx, tree[root << 1 | 1].mx);
}
void pushdown(int root, int left, int right)
{
int mid = left + right >> 1;
if (tree[root].lazy)
{
tree[root << 1].lazy += tree[root].lazy;
tree[root << 1].mx += tree[root].lazy;
tree[root << 1 | 1].lazy += tree[root].lazy;
tree[root << 1 | 1].mx += tree[root].lazy;
tree[root].lazy = 0;
}
}
void buildtree(int l, int r, int root)
{
tree[root].lazy = 0;
if (l == r)
{
tree[root].mx = a[l];
return;
}
int m = l + r >> 1;
buildtree(l, m, root << 1);
buildtree(m + 1, r, root << 1 | 1);
pushup(root);
}
// [L, R] all add num
void updata(int l, int r, int L, int R, int root, int num)
{
if (l >= L && r <= R)
{
tree[root].lazy += num;
tree[root].mx += num;
return;
}
pushdown(root, l, r);
int m = l + r >> 1;
if(L <= m) updata(l, m, L, R, root << 1, num);
if(R > m) updata(m + 1, r, L, R, root << 1 | 1, num);
pushup(root);
}
// query the max-number of [L, R]
int query(int l, int r, int L, int R, int root)
{
if (l >= L && r <= R) return tree[root].mx;
pushdown(root, l, r);
int m = l + r >> 1;
int res = 0;
if(L <= m) res = max(res, query(l, m, L, R, root << 1));
if(R > m) res = max(res, query(m + 1, r, L, R, root << 1 | 1));
pushup(root);
return res;
}
区间求和线段树
struct node {int sum = 0, lazy = 0;};
node tree[N << 2];
int a[N];
void pushup(int root)
{
tree[root].sum = tree[root << 1].sum + tree[root << 1 | 1].sum;
}
void pushdown(int root, int left, int right)
{
int mid = left + right >> 1;
if (tree[root].lazy)
{
tree[root << 1].lazy += tree[root].lazy;
tree[root << 1].sum += tree[root].lazy * (mid - left + 1);
tree[root << 1 | 1].lazy += tree[root].lazy;
tree[root << 1 | 1].sum += tree[root].lazy * (right - mid);
tree[root].lazy = 0;
}
}
void buildtree(int l, int r, int root)
{
tree[root].lazy = 0;
if (l == r)
{
tree[root].sum = a[l];
return;
}
int m = l + r >> 1;
buildtree(l, m, root << 1);
buildtree(m + 1, r, root << 1 | 1);
pushup(root);
}
// [L, R] all add num
void updata(int l, int r, int L, int R, int root, int num)
{
if (l >= L && r <= R)
{
tree[root].lazy += num;
tree[root].sum += num * (r - l + 1);
return;
}
if (l == r) return;
pushdown(root, l, r);
int m = l + r >> 1;
if(L <= m) updata(l, m, L, R, root << 1, num);
if(R > m) updata(m + 1, r, L, R, root << 1 | 1, num);
pushup(root);
}
// query the sum of [L, R]
int query(int l, int r, int L, int R, int root)
{
if (l >= L && r <= R) return tree[root].sum;
pushdown(root, l, r);
int m = l + r >> 1;
int res = 0;
if(L <= m) res += query(l, m, L, R, root << 1);
if(R > m) res += query(m + 1, r, L, R, root << 1 | 1);
pushup(root);
return res;
}
可持久化线段树 (求区间第k小值)
const int N = 2e5 + 10;
struct node
{
int l, r, sum;
}tree[N << 5];
int cnt, a[N], root[N];
vector<int> alls;
int find(int x) {return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;}
void insert(int l, int r, int pre, int &now, int p)
{
tree[++ cnt] = tree[pre];
now = cnt;
tree[now].sum ++;
if(l == r) return;
int mid = l + r >> 1;
if(p <= mid) insert(l, mid, tree[pre].l, tree[now].l, p);
else insert(mid + 1, r, tree[pre].r, tree[now].r, p);
}
int query(int l, int r, int L, int R, int k)
{
if(l == r) return l;
int mid = l + r >> 1;
int temp = tree[tree[R].l].sum - tree[tree[L].l].sum;
if(k <= temp) return query(l, mid, tree[L].l, tree[R].l, k);
return query(mid + 1, r, tree[L].r, tree[R].r, k - temp);
}
void solve()
{
int n, m; n = read(); m = read();
for(int i = 1;i <= n;i ++) a[i] = read();
for(int i = 1;i <= n;i ++) alls.push_back(a[i]);
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
for(int i = 1;i <= n;i ++) insert(1, n, root[i - 1], root[i], find(a[i]));
for(int i = 1;i <= m;i ++)
{
int l, r, k;
l = read(); r = read(), k = read();
printf("%d\n", alls[query(1, n, root[l - 1], root[r], k) - 1]);
}
}
可持久化线段树 (实现可持久化数组 查询及更新历史版本数组)
const int N = 1e6 + 10;
struct node
{
int l, r, val;
}tree[N << 5];
int cnt, root[N], a[N];
// now:初始建树版本的根节点
void build(int l, int r, int &now)
{
now = ++ cnt;
if(l == r)
{
tree[now].val = a[l];
return;
}
int m = l + r >> 1;
build(l, m, tree[now].l);
build(m + 1, r, tree[now].r);
}
// ver:历史版本根节点,now:当前版本根节点,pos:更新节点下标,num:更新的值
void modify(int l, int r, int ver, int &now, int pos, int num)
{
tree[now = ++ cnt] = tree[ver];
if(l == r)
{
tree[now].val = num;
return;
}
int m = l + r >> 1;
if(pos <= m) modify(l, m, tree[ver].l, tree[now].l, pos, num);
else modify(m + 1, r, tree[ver].r, tree[now].r, pos, num);
}
// now:查询版本的根节点,pos:查询节点的下标
int query(int l, int r, int now, int pos)
{
if(l == r) return tree[now].val;
int m = l + r >> 1;
if(pos <= m) return query(l, m, tree[now].l, pos);
return query(m + 1, r, tree[now].r, pos);
}
可持久化并查集
const int N = 2e5 + 10;
struct node
{
int l, r, val;
}tree[N << 5];
int cnt, rootfa[N], rootdep[N], n;
// now:初始建树版本的根节点
void build(int l, int r, int &now)
{
now = ++ cnt;
if(l == r)
{
tree[now].val = l;
return;
}
int m = l + r >> 1;
build(l, m, tree[now].l);
build(m + 1, r, tree[now].r);
}
// ver:历史版本根节点,now:当前版本根节点,pos:更新节点下标,num:更新的值
void modify(int l, int r, int ver, int &now, int pos, int num)
{
tree[now = ++ cnt] = tree[ver];
if(l == r)
{
tree[now].val = num;
return;
}
int m = l + r >> 1;
if(pos <= m) modify(l, m, tree[ver].l, tree[now].l, pos, num);
else modify(m + 1, r, tree[ver].r, tree[now].r, pos, num);
}
// now:查询版本的根节点,pos:查询节点的下标
int query(int l, int r, int now, int pos)
{
if(l == r) return tree[now].val;
int m = l + r >> 1;
if(pos <= m) return query(l, m, tree[now].l, pos);
return query(m + 1, r, tree[now].r, pos);
}
// 查询第ver个版本x结点的根节点(不能使用路径压缩)
int find(int ver, int x)
{
int rx = query(1, n, rootfa[ver], x);
if(rx == x) return x;
return find(ver, rx);
}
// 按秩合并ver版本的 x 与 y 集合
void merge(int ver, int x, int y)
{
x = find(ver - 1, x);
y = find(ver - 1, y);
if(x == y)
{
rootfa[ver] = rootfa[ver - 1];
rootdep[ver] = rootdep[ver - 1];
return;
}
int depx = query(1, n, rootdep[ver - 1], x);
int depy = query(1, n, rootdep[ver - 1], y);
if(depx < depy)
{
modify(1, n, rootfa[ver - 1], rootfa[ver], x, y);
rootdep[ver] = rootdep[ver - 1];
}
else if(depx > depy)
{
modify(1, n, rootfa[ver - 1], rootfa[ver], y, x);
rootdep[ver] = rootdep[ver - 1];
}
else
{
modify(1, n, rootfa[ver - 1], rootfa[ver], x, y);
modify(1, n, rootdep[ver - 1], rootdep[ver], y, depy + 1);
}
}
void solve()
{
n = read();
int m = read();
build(1, n, rootfa[0]);
for(int i = 1;i <= m;i ++)
{
int opt = read();
int a, b, k;
switch (opt)
{
case 1: // 当前版本基础上合并a b集合
a = read();
b = read();
merge(i, a, b);
break;
case 2: // 回溯到第k个版本
k = read();
rootfa[i] = rootfa[k];
rootdep[i] = rootdep[k];
break;
case 3: // 查询当前版本 a b 是否属于同一集合
a = read();
b = read();
rootfa[i] = rootfa[i - 1];
rootdep[i] = rootdep[i - 1];
int ra = find(i, a), rb = find(i, b);
if(ra == rb) printf("1\n");
else printf("0\n");
}
}
}
替罪羊树(平衡树 动态查询排名、分数、前驱和后继)
const int N = 1e5 + 10;
const double alpha = 0.75;
struct node
{
int l, r, val;
int size, fact; // size: 树的大小, fact:实际有效数字大小
bool exist; // 是否存在 1: 存在 0: 删除
}tree[N];
int cnt, root;
// 新建结点 赋值为val
void newnode(int &now, int val)
{
now = ++ cnt;
tree[now].l = tree[now].r = 0;
tree[now].val = val;
tree[now].size = tree[now].fact = 1;
tree[now].exist = true;
}
// 判断当前子树是否平衡
bool imbalance(int now)
{
if(std::max(tree[tree[now].l].size, tree[tree[now].r].size) > tree[now].size * alpha
|| tree[now].size - tree[now].fact > tree[now].size * 0.3)
return true;
return false;
}
std::vector<int> vec;
// 中序遍历序列存入数组vec
void dfs(int now)
{
if(!now) return;
dfs(tree[now].l);
if(tree[now].exist) vec.push_back(now);
dfs(tree[now].r);
}
// 对vec[l, r] 区间二分建平衡树
void lift(int l, int r, int &now)
{
if(l == r)
{
now = vec[l];
tree[now].l = tree[now].r = 0;
tree[now].size = tree[now].fact = 1;
tree[now].exist = true;
return;
}
int m = l + r >> 1;
while(l < m && tree[vec[m]].val == tree[vec[m - 1]].val) m --;
now = vec[m];
if(l < m) lift(l, m - 1, tree[now].l);
else tree[now].l = 0;
lift(m + 1, r, tree[now].r);
tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
tree[now].fact = tree[tree[now].l].fact + tree[tree[now].r].fact + 1;
}
// 暴力重建 now 结点为根的子树
void rebuild(int &now)
{
vec.clear();
dfs(now);
if(vec.empty())
{
now = 0;
return;
}
lift(0, vec.size() - 1, now);
}
// 向上更新树的大小 - size
void updata(int now, int end)
{
if(!now) return;
if(tree[end].val < tree[now].val) updata(tree[now].l, end);
else updata(tree[now].r, end);
tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
}
// 由顶至下检查是否平衡
void check(int &now, int end)
{
if(now == end) return;
if(imbalance(now))
{
rebuild(now);
updata(root, now);
return;
}
if(tree[end].val < tree[now].val) check(tree[now].l, end);
else check(tree[now].r, end);
}
// 向平衡树中插入一个数
void insert(int &now, int val)
{
if(!now)
{
newnode(now, val);
check(root, now);
return;
}
tree[now].size ++;
tree[now].fact ++;
if(val < tree[now].val) insert(tree[now].l, val);
else insert(tree[now].r, val);
}
// 在平衡树中删除一个数
void del(int now, int val)
{
if(tree[now].exist && tree[now].val == val)
{
tree[now].exist = false;
tree[now].fact --;
check(root, now);
return;
}
tree[now].fact --;
if(val < tree[now].val) del(tree[now].l, val);
else del(tree[now].r, val);
}
// 获得排名(排名定义为比当前数小的数的个数 +1)
int getrank(int val)
{
int now = root, rank = 1;
while(now)
{
if(val <= tree[now].val) now = tree[now].l;
else
{
rank += tree[tree[now].l].fact + tree[now].exist;
now = tree[now].r;
}
}
return rank;
}
// 查询排名为 rank 的数
int getnum(int rank)
{
int now = root;
while(now)
{
if(tree[now].exist && tree[tree[now].l].fact + tree[now].exist == rank)
break;
else if(tree[tree[now].l].fact >= rank)
now = tree[now].l;
else
{
rank -= tree[tree[now].l].fact + tree[now].exist;
now = tree[now].r;
}
}
return tree[now].val;
}
// 查询值为 x 的前驱
int get_pre(int x)
{
return getnum(getrank(x) - 1);
}
// 查询值为 x 的后继
int get_next(int x)
{
return getnum(getrank(x + 1));
}
treap (树旋转)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
int l, r;
int key, val;
int size, cnt;
}treap[N];
int root, idx;
int get_node(int key)
{
treap[++ idx].key = key;
treap[idx].val = rand();
treap[idx].size = treap[idx].cnt = 1;
return idx;
}
void pushup(int p)
{
treap[p].size = treap[treap[p].l].size + treap[treap[p].r].size + treap[p].cnt;
}
void zig(int &p) // 右旋
{
int q = treap[p].l;
treap[p].l = treap[q].r, treap[q].r = p, p = q;
pushup(treap[p].r), pushup(p);
}
void zag(int &p) // 左旋
{
int q = treap[p].r;
treap[p].r = treap[q].l, treap[q].l = p, p = q;
pushup(treap[p].l), pushup(p);
}
void build()
{
root = get_node(-inf);
treap[root].r = get_node(inf);
pushup(root);
if(treap[root].val < treap[treap[root].r].val) zag(root);
}
void insert(int &p, int key)
{
if(!p) p = get_node(key);
else if(treap[p].key == key) treap[p].cnt ++;
else if(treap[p].key > key)
{
insert(treap[p].l, key);
if(treap[treap[p].l].val > treap[p].val) zig(p);
}
else
{
insert(treap[p].r, key);
if(treap[treap[p].r].val > treap[p].val) zag(p);
}
pushup(p);
}
void remove(int &p, int key)
{
if(!p) return;
if(treap[p].key == key)
{
if(treap[p].cnt > 1) treap[p].cnt --;
else if(treap[p].l || treap[p].r)
{
if(!treap[p].r || treap[treap[p].l].val > treap[treap[p].r].val)
{
zig(p);
remove(treap[p].r, key);
}
else
{
zag(p);
remove(treap[p].l, key);
}
}
else p = 0;
}
else if(treap[p].key > key)
remove(treap[p].l, key);
else
remove(treap[p].r, key);
pushup(p);
}
int get_rank_by_key(int p, int key)
{
if(!p) return 0;
if(treap[p].key == key) return treap[treap[p].l].size + 1;
if(treap[p].key > key) return get_rank_by_key(treap[p].l, key);
return treap[treap[p].l].size + treap[p].cnt + get_rank_by_key(treap[p].r, key);
}
int get_key_by_rank(int p, int rank)
{
if(!p) return inf;
if(treap[treap[p].l].size >= rank) return get_key_by_rank(treap[p].l, rank);
if(treap[treap[p].l].size + treap[p].cnt >= rank) return treap[p].key;
return get_key_by_rank(treap[p].r, rank - treap[treap[p].l].size - treap[p].cnt);
}
int get_pre_by_key(int p, int key) // 严格小于key的最大数
{
if(!p) return -inf;
if(treap[p].key >= key) return get_pre_by_key(treap[p].l, key); // 去掉等号变成不严格
return max(treap[p].key, get_pre_by_key(treap[p].r, key));
}
int get_next_by_key(int p, int key) //严格大于key的最小数
{
if(!p) return inf;
if(treap[p].key <= key) return get_next_by_key(treap[p].r, key); // 去掉等号变成不严格
return min(treap[p].key, get_next_by_key(treap[p].l, key));
}
int main()
{
int n; cin>>n;
build();
for(int i = 1;i <= n;i ++)
{
int opt, x; cin>>opt>>x;
if(opt == 1) insert(root, x);
else if(opt == 2) remove(root, x);
else if(opt == 3) cout<<get_rank_by_key(root, x) - 1<<endl; // 有两个哨兵结点(-inf, inf)
else if(opt == 4) cout<<get_key_by_rank(root, x + 1)<<endl; // 排名对应原本会多1
else if(opt == 5) cout<<get_pre_by_key(root, x)<<endl;
else cout<<get_next_by_key(root, x)<<endl;
}
return 0;
}
fhq treap(按值合并 平衡树 动态查询排名、分数、前驱和后继)
const int N = 1e5 + 10;
struct node
{
int l, r;
int val, key; // val: 结点值, key: 结点标记值(维护二叉堆)
int size; // 树的大小
}tree[N];
int cnt, root;
// 新建结点 赋值为val
std::mt19937 rnd(233);
inline int newnode(int val)
{
tree[++ cnt].val = val;
tree[cnt].key = rnd();
tree[cnt].size = 1;
return cnt;
}
// 向上更新树的大小 - size
inline void updata(int now)
{
tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
}
// 将平衡树 按值 分裂为两棵树 根分别为 x, y ;
// x 树上的值全部小于等于 val, y 树上的值全部大于 val
void split(int now, int val, int &x, int &y)
{
if(!now) x = y = 0;
else
{
if(tree[now].val <= val)
{
x = now;
split(tree[now].r, val, tree[now].r, y);
}
else
{
y = now;
split(tree[now].l, val, x, tree[now].l);
}
updata(now);
}
}
// 将两颗平衡树合并为一颗平衡树 返回根节点
int merge(int x, int y)
{
if(!x || !y) return x + y;
if(tree[x].key > tree[y].key)
{
tree[x].r = merge(tree[x].r, y);
updata(x);
return x;
}
else
{
tree[y].l = merge(x, tree[y].l);
updata(y);
return y;
}
}
int x, y, z;
// 向平衡树中插入一个数
inline void insert(int val)
{
split(root, val, x, y);
root = merge(merge(x, newnode(val)), y);
}
// 在平衡树中删除一个数
inline void del(int val)
{
split(root, val, x, z);
split(x, val - 1, x, y);
y = merge(tree[y].l, tree[y].r);
root = merge(merge(x, y), z);
}
// 获得排名(排名定义为比当前数小的数的个数 +1)
inline int getrank(int val)
{
split(root, val - 1, x, y);
int res = tree[x].size + 1;
root = merge(x, y);
return res;
}
// 查询排名为 rank 的数
inline int getnum(int rank)
{
int now = root;
while(now)
{
if(tree[tree[now].l].size + 1 == rank) break;
else if(tree[tree[now].l].size >= rank) now = tree[now].l;
else
{
rank -= tree[tree[now].l].size + 1;
now = tree[now].r;
}
}
return tree[now].val;
}
// 查询值为 val 的前驱
inline int get_pre(int val)
{
split(root, val - 1, x, y);
int now = x;
while(tree[now].r) now = tree[now].r;
int res = tree[now].val;
root = merge(x, y);
return res;
}
// 查询值为 val 的后继
inline int get_next(int val)
{
split(root, val, x, y);
int now = y;
while(tree[now].l) now = tree[now].l;
int res = tree[now].val;
root = merge(x, y);
return res;
}
fhq treap(按大小合并 维护区间的动态分裂,合并和翻转)
const int N = 1e5 + 10;
struct node
{
int l, r;
int val, key; // val: 结点值, key: 结点标记值(维护二叉堆)
int size; // 树的大小
bool reverse; // 区间是否被翻转
}tree[N];
int cnt, root;
// 新建结点 赋值为val
std::mt19937 rnd(233);
inline int newnode(int val)
{
tree[++ cnt].val = val;
tree[cnt].key = rnd();
tree[cnt].size = 1;
return cnt;
}
// 向上更新树的大小 - size
inline void update(int now)
{
tree[now].size = tree[tree[now].l].size + tree[tree[now].r].size + 1;
}
// 向下传递懒标记 是否翻转 - reverse
inline void pushdown(int now)
{
if(tree[now].reverse)
{
std::swap(tree[now].l, tree[now].r);
tree[tree[now].l].reverse ^= 1;
tree[tree[now].r].reverse ^= 1;
tree[now].reverse = false;
}
}
// 将平衡树 按大小 分裂为两棵树 根分别为 x, y ;
// x 树的大小等于 siz, y 树为剩下的部分
void split(int now, int siz, int &x, int &y)
{
if(!now) x = y = 0;
else
{
pushdown(now);
if(tree[tree[now].l].size < siz)
{
x = now;
split(tree[now].r, siz - tree[tree[now].l].size - 1, tree[now].r, y);
}
else
{
y = now;
split(tree[now].l, siz, x, tree[now].l);
}
update(now);
}
}
// 将两颗平衡树合并为一颗平衡树 返回根节点
int merge(int x, int y)
{
if(!x || !y) return x + y;
if(tree[x].key < tree[y].key)
{
pushdown(x);
tree[x].r = merge(tree[x].r, y);
update(x);
return x;
}
else
{
pushdown(y);
tree[y].l = merge(x, tree[y].l);
update(y);
return y;
}
}
// 翻转 [l, r] 区间
void reverse(int l, int r)
{
int x, y, z;
split(root, l - 1, x, y);
split(y, r - l + 1, y, z);
tree[y].reverse ^= 1;
root = merge(merge(x, y), z);
}
// 中序遍历输出
void output(int now)
{
if(!now) return;
pushdown(now);
output(tree[now].l);
printf("%d ", tree[now].val);
output(tree[now].r);
}
// merge(root, newnode(val)) 为向数组尾插入一个值为val的元素
// 将数组初始化为[1, n]的操作代码为:
for(int i = 1;i <= n;i ++) root = merge(root, newnode(i));
最小圆覆盖
const int N = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
struct node
{
double x, y;
} e[N], o; //点集, 圆心
int n;
double ans = inf, r = 0;
double get_dis(node a, node b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
// 三个点中任意选取两对点之间连两条线段,它们垂直平分线的交点就是圆心
void getr(node p1, node p2, node p3)
{
double a, b, c, d, e, f;
a = p2.y - p1.y;
b = p3.y - p1.y;
c = p2.x - p1.x;
d = p3.x - p1.x;
f = p3.x * p3.x + p3.y * p3.y - p1.x * p1.x - p1.y * p1.y;
e = p2.x * p2.x + p2.y * p2.y - p1.x * p1.x - p1.y * p1.y;
o.x = (a * f - b * e) / (2 * a * d - 2 * b * c);
o.y = (d * e - c * f) / (2 * a * d - 2 * b * c);
r = get_dis(o, p1);
}
int circular()
{
random_shuffle(e + 1, e + 1 + n);
o.x = e[1].x, o.y = e[1].y, r = 0;
for (int i = 2; i <= n; i++)
{
if (get_dis(e[i], o) > r + eps)
{
o.x = e[i].x, o.y = e[i].y, r = 0;
for (int j = 1; j < i; j++)
{
if (get_dis(e[j], o) > r + eps)
{
o.x = (e[i].x + e[j].x) / 2;
o.y = (e[i].y + e[j].y) / 2;
r = get_dis(o, e[i]);
for (int k = 1; k < j; k++)
{
if (get_dis(e[k], o) > r + eps)
getr(e[i], e[j], e[k]);
}
}
}
}
}
return r;
}
固定半径圆 最大覆盖点数
const int N = 1e2 + 10;
const double eps = 1e-8;
struct Point
{
double x, y;
Point(){}
Point(double tx, double ty) {x = tx; y = ty;}
}a[N];
double get_dist(Point p1, Point p2)
{
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
// 求过两点的半径为 r 的圆,返回圆心
Point get_circle(Point p1, Point p2, double r)
{
Point mid = Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
double angle = atan2 (p1.x - p2.x,p2.y - p1.y);
double dist = sqrt(r * r - get_dist(mid, p1) * get_dist(mid, p1));
return Point(mid.x + dist * cos(angle), mid.y + dist * sin(angle));
}
// 求点集 p 中 半径为 r 的圆覆盖点的最大数量
int CircleAndPoints(Point p[], int n, double r)
{
int res = 1;
if(n == 1) return res;
for(int i = 1;i <= n;i ++) for(int j = i + 1;j <= n;j ++)
{
Point central = get_circle(p[i], p[j], r);
int cnt = 0;
for(int k = 1;k <= n;k ++) if(get_dist(p[k], central) < r + eps) cnt ++;
res = max(res, cnt);
}
return res;
}
Graham's Scan算法(求点集的凸包)
const int N = 1e3 + 10;
struct node
{
double x, y;
}p[N], P[N]; // p 代表初始点集, P 代表凸包上的点集
// 叉积
double X(node A, node B, node C)
{
return (B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y);
}
// 求两点间距离
double get_dist(node A, node B)
{
return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
// 按斜率从小到大排序
bool cmp(node A, node B)
{
double pp = X(p[0], A, B);
if(pp > 0) return true;
if(pp < 0) return false;
return get_dist(p[0], A) < get_dist(p[0], B);
}
void solve()
{
int n = read();
for(int i = 0;i < n;i ++) p[i].x = read(), p[i].y = read();
if(n == 1) printf("%.2f\n", 0.000);
else if(n == 2) printf("%.2f\n", get_dist(p[0], p[1]));
else
{
for(int i = 0;i < n;i ++)
{
if(p[i].y < p[0].y) swap(p[0], p[i]);
else if(p[0].y == p[i].y && p[i].x < p[0].x) swap(p[0], p[i]);
}
sort(p + 1, p + n, cmp);
P[0] = p[0]; P[1] = p[1];
int tot = 1;
for(int i = 2;i < n;i ++)
{
while(tot > 0 && X(P[tot - 1], P[tot], p[i]) <= 0) tot --;
P[++ tot] = p[i];
}
double ans = 0;
for(int i = 0;i < tot;i ++) ans += get_dist(P[i], P[i + 1]);
ans += get_dist(P[0], P[tot]);
printf("%.2f\n", ans);
}
}
从子集的和还原数组 // 可包含负数元素
vector<int> recoverArray(int n, vector<int>& sums)
{
int mn = 1e5;
for(auto &p : sums) mn = min(mn, p);
mn = -mn;
multiset<int> st;
for(auto &p : sums) st.insert(p + mn);
st.erase(st.begin());
vector<int> ans;
ans.push_back(*st.begin());
for(int i = 1;i < n;i ++)
{
for(int k = 0;k < (1 << i);k ++) if(k >> (i - 1) & 1){
int temp = 0;
for(int j = 0;j < i;j ++) if(k >> j & 1) temp += ans[j];
st.erase(st.find(temp));
}
ans.push_back(*st.begin());
}
for(int i = 0;i < (1 << n);i ++)
{
int temp = 0;
for(int j = 0;j < n;j ++) if(i >> j & 1) temp += ans[j];
if(temp == mn)
{
for(int j = 0;j < n;j ++) if(i >> j & 1) ans[j] = -ans[j];
break;
}
}
return ans;
}
组合数学
(1)Cn0+Cn2+Cn4+……+Cnn=2^(n-1)(n为偶数)
(2)Cn1+Cn3+Cn5+……+Cn(n-1)=2^(n-1) (n为偶数)
(3)Cn0+Cn2+Cn4+……+Cn(n-1)=2^(n-1) (n为奇数)
(4)Cn1+Cn3+Cn5+……+Cnn=2^(n-1) (n为奇数)
(5)Cn0-Cn1+Cn2-Cn3+……+(-1)^nCnn=0
具体数学
12 + 22 + 32 + ··· + n2 = n * (n + 1) * (n * 2 + 1) / 6
莫队 (离线查询多个区间不同数的个数)
struct query
{
int l, r, id;
}q[N];
int n, m, sz, bnum, now;
int a[N], ans[N], cnt[N], belong[N];
bool cmp(query a, query b)
{
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
void solve()
{
n = read(); // 数组大小
sz = (int)sqrt(n);
bnum = ceil((double)(n) / sz);
for(int i = 1;i <= n;i ++) cnt[i] = 0;
now = 0;
for(int i = 1;i <= bnum;++ i) for(int j = (i - 1) * sz + 1;j <= i * sz;++ j)
belong[j] = i;
for(int i = 1;i <= n;i ++) a[i + n] = a[i] = read(); // 数值过大需要先对数据进行离散化
m = read(); // 区间查询数
for(int i = 1;i <= m;i ++)
{
q[i].l = read();
q[i].r = read();
q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int l = 1, r = 0;
for(int i = 1;i <= m;i ++)
{
int ql = q[i].l, qr = q[i].r;
while(l < ql) now -= !--cnt[a[l++]];
while(l > ql) now += !cnt[a[--l]]++;
while(r < qr) now += !cnt[a[++r]]++;
while(r > qr) now -= !--cnt[a[r--]];
ans[q[i].id] = now;
}
for(int i = 1;i <= m;i ++) printf("%d\n", ans[i]);
}
长度不小于len的子数组的最大平均值
double sum[N];
// 数组 p, 数组长度 n, 子数组最小长度 len, 测试平均值 x;
bool check(int p[], int n, int len, double x)
{
for(int i = 1;i <= n;i ++) sum[i] = sum[i - 1] + p[i] - x;
double mn = 1e5;
for(int i = len, j = 0;i <= n;i ++, j ++)
{
mn = min(mn, sum[j]);
if(sum[i] - mn >= 0) return true;
}
return false;
}
// 数组 p, 数组长度 n, 子数组最小长度 len, 元素最小值 left, 元素最大值 right
double get_average(int p[], int n, int len, double left, double right)
{
while(right - left > 1e-7)
{
double mid = (left + right) / 2;
if(check(p, n, len, mid)) left = mid;
else right = mid;
}
return right;
}
表达式求值(可带括号,首个数字不能含前导符号,)
// 计算过程中所有中间结果不得超过 int 范围,若有可能越界手动改为long long
inline int expre(string &s)
{
stack<int> sta1;
stack<char> sta2;
for(int i = 0;i < s.length();i ++)
{
if(s[i] >= '0' && s[i] <= '9')
{
int j = i, num = 0;
while(j < s.length() && s[j] >= '0' && s[j] <= '9')
num = num * 10 + s[j ++] - '0';
i = j - 1;
if(!sta2.empty() && (sta2.top() == '*' || sta2.top() == '/'))
{
char opt = sta2.top(); sta2.pop();
int temp = sta1.top(); sta1.pop();
if(opt == '*') sta1.push(temp * num);
else sta1.push(temp / num);
}
else sta1.push(num);
}
else if(s[i] == ')')
{
int sum = 0;
while(sta2.top() != '(')
{
int num = sta1.top(); sta1.pop();
int opt = sta2.top(); sta2.pop();
if(opt == '+') sum += num;
else sum -= num;
}
sta2.pop();
sum += sta1.top(); sta1.pop();
sta1.push(sum);
if(!sta2.empty() && (sta2.top() == '*' || sta2.top() == '/'))
{
int num2 = sta1.top(); sta1.pop();
int num1 = sta1.top(); sta1.pop();
int opt = sta2.top(); sta2.pop();
if(opt == '*') sta1.push(num1 * num2);
else sta1.push(num1 / num2);
}
}
else sta2.push(s[i]);
}
int sum = 0;
while(!sta2.empty())
{
int num = sta1.top(); sta1.pop();
int opt = sta2.top(); sta2.pop();
if(opt == '+') sum += num;
else sum -= num;
}
return sta1.top() + sum;
}
高斯消元求解行列式
int det(int n) {
int res = 1;
for(int i = 1;i <= n;i ++)
{
int qaq = quickpow(a[i][i], mod2 - 2);
for(int j = i + 1;j <= n;j ++)
{
int tmp = 1ll * a[j][i] * qaq % mod2;
for(int k = 1;k <= n;k ++)
a[j][k] = (a[j][k] - 1ll * a[i][k] * tmp % mod2 + mod2) % mod2;
}
}
for(int i = 1;i <= n;i ++) res = 1ll * res * a[i][i] % mod2;
return (res % mod2 + mod2) % mod2;
}
LCA 倍增
vector<int> e[N];
int depth[N], fa[N][16];
void bfs(int root) // 预处理 depth 和 fa
{
memset(depth, 0x3f, sizeof depth);
depth[0] = 0, depth[root] = 1;
queue<int> que;
que.push(root);
while(!que.empty())
{
int u = que.front(); que.pop();
for(auto &v : e[u])
{
if(depth[v] > depth[u] + 1)
{
depth[v] = depth[u] + 1;
que.push(v);
fa[v][0] = u;
for(int i = 1;i <= 15;i ++)
fa[v][i] = fa[fa[v][i - 1]][i - 1];
}
}
}
}
int lca(int x, int y) // 查询 x, y 的最近公共祖先
{
if(depth[x] < depth[y]) swap(x, y);
for(int i = 15;i >= 0;i --)
if(depth[fa[x][i]] >= depth[y])
x = fa[x][i];
if(x == y) return x;
for(int i = 15;i >= 0;i --)
if(fa[x][i] != fa[y][i])
{
x = fa[x][i];
y = fa[y][i];
}
return fa[x][0];
}
int main()
{
int n; cin>>n;
int root = 0;
for(int i = 1;i <= n;i ++)
{
int u, v; cin>>u>>v;
if(v == -1) root = u;
else e[u].push_back(v), e[v].push_back(u);
}
bfs(root);
int m; cin>>m;
for(int i = 1;i <= m;i ++)
{
int x, y; cin>>x>>y;
int tot = lca(x, y);
}
return 0;
}
LCA tarjan 算法
vector<int> e[N];
vector<pair<int, int>> query[N];
pair<int, int> Data[M];
int parent[N];
int p[M], st[N];
int find(int x)
{
if(parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
void tarjan(int u)
{
st[u] = 1;
for(auto &v : e[u])
{
if(!st[v])
{
tarjan(v);
parent[v] = u;
}
}
for(auto &item : query[u])
{
int v = item.first, id = item.second;
if(st[v] == 2) p[id] = find(v);
}
st[u] = 2;
}
int main()
{
int n; cin>>n;
for(int i = 1;i < N;i ++) parent[i] = i;
int root = 0;
for(int i = 1;i <= n;i ++)
{
int u, v; cin>>u>>v;
if(v == -1) root = u;
else
{
e[u].push_back(v);
e[v].push_back(u);
}
}
int m; cin>>m;
for(int i = 1;i <= m;i ++)
{
int x, y; cin>>x>>y;
query[x].push_back(make_pair(y, i));
query[y].push_back(make_pair(x, i));
Data[i] = make_pair(x, y);
}
tarjan(root);
return 0;
}
整除分块
for(int l = 1, r;l <= n;l = r + 1)
{
r = n / (n / l);
ans += (r - l + 1) * (n / l);
}
AC自动机(trie图)
// 字典集中每个模式串在匹配串中出现次数
struct node
{
int fail;
int ch[26];
int ans;
};
struct node NIL = {0};
struct trie
{
vector<node> tr;
vector<int> in;
map<int, int> ref;
int siz = 0;
void insert(string s,int x)
{
if(!tr.size())
tr.push_back(NIL);
int place = 0;
for (int i = 0; i < s.length();i++)
{
if (tr[place].ch[s[i] - 97] == 0)
{
tr[place].ch[s[i] - 97] = ++siz;
tr.push_back(NIL);
in.push_back(0);
}
place = tr[place].ch[s[i] - 97];
// tr[place].ans ++; 统计每个字典串在字典中出现的次数,计数后topo()
}
ref[x] = place;
}
void build()
{
queue<int> q;
for (int i = 0; i < 26;i++)
if(tr[0].ch[i])
q.push(tr[0].ch[i]);
while(!q.empty())
{
int tp = q.front();
q.pop();
for (int i = 0; i < 26;i++)
if(tr[tp].ch[i])
{
tr[tr[tp].ch[i]].fail = tr[tr[tp].fail].ch[i];
in[tr[tr[tp].fail].ch[i]]++;
q.push(tr[tp].ch[i]);
}
else
tr[tp].ch[i] = tr[tr[tp].fail].ch[i];
}
}
void query(string t)
{
int place = 0;
for (int i = 0; i < t.length();i++)
{
place = tr[place].ch[t[i] - 97];
tr[place].ans++;
}
}
void topo()
{
queue<int> q;
for (int i = 1; i <= siz;i++)
if(!in[i])
q.push(i);
while(!q.empty())
{
int tp = q.front();
q.pop();
tr[tr[tp].fail].ans += tr[tp].ans;
in[tr[tp].fail]--;
if(!in[tr[tp].fail])
q.push(tr[tp].fail);
}
//for (int i = 0; i <= siz;i++)
// printf("ANS:%d %d\n", i, tr[i].ans);
}
};
void solve()
{
int n; cin>>n;
trie tr;
for(int i = 1;i <= n;i ++)
{
string t; cin>>t;
tr.insert(t, i);
}
tr.build();
string s; cin>>s;
tr.query(s);
tr.topo();
for(int i = 1;i <= n;i ++)
cout<<tr.tr[tr.ref[i]].ans<<endl;
}
signed main()
{
ios;
int t = 1; // cin>>t;
while(t --) solve();
return 0;
}