文章目录

  • 树上倍增
  • 分治
    • CDQ分治
      • 三维偏序
      • 动态逆序对
  • 分块
    • 莫队算法
    • 带修莫队
    • 带修莫队取MEX
    • 单调增加莫队(回滚莫队&不删除莫队)
    • 树上莫队
    • 树上带修莫队
  • 启发式搜索(玄学)
    • 模拟退火
    • 差分进化算法
    • A*算法 (启发式 BFS)
    • IDA* 算法(启发式DFS)

树上倍增

求 k k k 级祖先。

//   P5903 【模板】树上 k 级祖先
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr)
typedef long long LL;
const int maxn = 5e5 + 5;
const int MN = INT_MIN;
const int MX = INT_MAX;int fa[maxn][20], d[maxn];
LL last, ans;
unordered_map<int, int> c;inline int dfs(int x){if(d[x]) return d[x];return d[x] = dfs(fa[x][0]) + 1;
}unsigned int s;inline unsigned int get(unsigned int x) {x ^= x << 13;x ^= x >> 17;x ^= x << 5;return s = x;
}inline int lowbit(int x){return x & (-x);
}int main() {IOS;for(int i = 0; i < 20; i++) c[1 << i] = i;int n, q, x, k;cin >> n >> q >> s;for(int i = 1; i <= n; i++){cin >> fa[i][0];if(fa[i][0] == 0){d[i] = 1;}}for(int j = 1; j < 20; j++){for(int i = 1; i <= n; i++){fa[i][j] = fa[fa[i][j - 1]][j - 1];}}for(int i = 1; i <= n; i++) dfs(i);for(int i = 1; i <= q; i++){x = (get(s) ^ last) % n + 1;k = (get(s) ^ last) % d[x];
//        cout << x << " " << k << endl;int now = x;while(k){now = fa[now][c[lowbit(k)]];k -= lowbit(k);}ans ^= (LL)i * now;last = now;}cout << ans << endl;
}

分治

//求逆序对
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
const int maxn = 5e5 + 5;
int a[maxn], b[maxn];
LL ans;inline void mergesort(int l, int r){if(l >= r) return;int mid = l + r >> 1;mergesort(l, mid);mergesort(mid + 1, r);int i = l, j = mid + 1, k = l;while(i <= mid && j <= r){if(a[i] <= a[j]) b[k++] = a[i++];else{b[k++] = a[j++];ans += mid - i + 1;}}while(i <= mid) b[k++] = a[i++];while(j <= r) b[k++] = a[j++];for(i = l; i <= r; i++) a[i] = b[i];
}int main(){IOS;int n;cin >> n;for(int i = 1; i <= n; i++) cin >> a[i];mergesort(1, n);cout << ans << endl;
}

CDQ分治

三维偏序

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 5;struct node{int a, b, c, cnt, f;bool operator <(const node &x) const{return a == x.a ? (b == x.b ? c < x.c : b < x.b) : a < x.a;}
}now[maxn], temp[maxn];int tree[maxn], m, ans[maxn];inline int lowbit(int x){return x & (-x);}inline void add(int x, int c){while(x <= m){tree[x] += c;x += lowbit(x);}
}inline int sum(int x){int ans = 0;while(x){ans += tree[x];x -= lowbit(x);}return ans;
}inline void cdq(int l, int r){if(l == r) return;int mid = l + r >> 1;cdq(l, mid); cdq(mid + 1, r);int i = l, j = mid + 1, k = l;while(i <= mid && j <= r){if(now[i].b <= now[j].b){add(now[i].c, now[i].cnt);temp[k++] = now[i++];}else{now[j].f += sum(now[j].c);  // now cal the c lower than j.ctemp[k++] = now[j++];}}while(i <= mid){add(now[i].c, now[i].cnt);temp[k++] = now[i++];}while(j <= r){now[j].f += sum(now[j].c);      // now cal the c lower than j.ctemp[k++] = now[j++];}for(i = l; i <= mid; i++) add(now[i].c, -now[i].cnt);for(i = l; i <= r; i++) now[i] = temp[i];
}int main(){IOS;int n, cnt = 1;cin >> n >> m;for(int i = 1; i <= n; i++){cin >> now[i].a >> now[i].b >> now[i].c;now[i].cnt = 1;}sort(now + 1, now + 1 + n);for(int i = 2; i <= n; i++){if(now[cnt].a == now[i].a && now[cnt].b == now[i].b && now[cnt].c == now[i].c) now[cnt].cnt++;else now[++cnt] = now[i];}cdq(1, cnt);for(int i = 1; i <= cnt; i++) ans[now[i].cnt + now[i].f - 1] += now[i].cnt;for(int i = 0; i < n; i++) cout << ans[i] << endl;
}

动态逆序对

逐个删除 m m m 个元素,求每次删除前的逆序对个数。

三维元素:位置 x x x,时间 t t t,值 v v v。给定序列已经按 x x x 排好, x < x , x <x^, x<x,。则归并排序 t t t,对于每个元素求贡献,同时求解左侧 t t t 小, v v v 大的和右侧 t t t 小, v v v 小的。

逆序对和三维偏序不同之处在于,当 x < x , x<x^, x<x, 时, y < y , , z > z , y<y^,,z>z^, y<y,,z>z, 以及 y > y , , z < z , y>y^,,z<z^, y>y,,z<z, 都是逆序对,而偏序关系只有一边 y < y , , z < z , y<y^,,z<z^, y<y,,z<z,。

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;LL ans[maxn];
int tree[maxn], n, vis[maxn];struct node{int v, t;
}now[maxn], temp[maxn];inline int lowbit(int x){return x & (-x);}inline void add(int x, int c){while(x <= n){tree[x] += c;x += lowbit(x);}
}inline int sum(int x){int ans = 0;while(x){ans += tree[x];x -= lowbit(x);}return ans;
}inline void cdq(int l, int r){if(l == r) return;int mid = l + r >> 1;cdq(l, mid);cdq(mid + 1, r);int i = l, j = mid + 1, k = l;while(i <= mid && j <= r){if(now[i].t <= now[j].t){add(now[i].v, 1);temp[k++] = now[i++];}else{ans[now[j].t] += sum(n) - sum(now[j].v);temp[k++] = now[j++];}}while(i <= mid){add(now[i].v, 1);temp[k++] = now[i++];}while(j <= r){ans[now[j].t] += sum(n) - sum(now[j].v);temp[k++] = now[j++];}for(i = l; i <= mid; i++) add(now[i].v, -1);i = l, j = mid + 1;while(i <= mid && j <= r){if(now[i].t >= now[j].t) add(now[j++].v, 1);else{ans[now[i].t] += sum(now[i].v);i++;}}while(i <= mid){ans[now[i].t] += sum(now[i].v);i++;}while(j <= r) add(now[j++].v, 1);for(i = mid + 1; i <= r; i++) add(now[i].v, -1);for(i = l; i <= r; i++) now[i] = temp[i];
}int main(){IOS;int m, cnt, b;LL tol = 0;cin >> n >> m;for(int i = 1; i <= n; i++)cin >> now[i].v;cnt = n;for(int i = 1; i <= m; i++){cin >> b;vis[b] = cnt--;}for(int i = 1; i <= n; i++){now[i].t = vis[now[i].v];if(!now[i].t) now[i].t = cnt--;}cdq(1, n);for(int i = 1; i <= n; i++) ans[i] += ans[i - 1];for(int i = n; i > n - m; i--) cout << ans[i] << endl;
}

分块

莫队算法

// 求区间内任选两个数字相同的概率   洛谷 P1494
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL k[50005],w[50005],tw[50005];
struct query{LL l, r, id;LL a, b;bool operator <(const query &a)const{return k[l] < k[a.l] || k[l] == k[a.l] && r < a.r;}
}q[50005];  // 精髓就是这个分块排序bool cmpid(const query &a,const query &b){return a.id < b.id;
}LL gcd(LL x,LL y){return y == 0 ? x : gcd(y, x % y);
}int change(LL &ans, LL pos, int flag){pos = tw[pos];if(flag){ans += 2*w[pos];w[pos]++;}else{ans += 2*(1-w[pos]);w[pos]--;}
}int main(){int n, m, i;cin >> n >> m;int kuai = (int)sqrt(double(n) + 0.5);for(i = 1; i <= n; i++)k[i] = (i - 1)/kuai + 1;for(i = 1; i <= n; i++)cin >> tw[i];for(i = 1; i <= m; i++){cin >> q[i].l >> q[i].r;q[i].id=i;}sort(q + 1, q + 1 + m);LL ans = 0;LL l = 1, r = 0, a;for(i = 1; i <= m; i++){while(q[i].r > r)change(ans, ++r, 1);while(q[i].r < r)change(ans, r--, 0);while(q[i].l > l)change(ans, l++, 0);while(q[i].l < l)change(ans, --l, 1);if(q[i].r! = q[i].l){q[i].a = ans;q[i].b = (q[i].r - q[i].l + 1)*(q[i].r - q[i].l);a = gcd(q[i].a, q[i].b);q[i].a /= a;q[i].b /= a;}else{q[i].a = 0;q[i].b = 1;}}sort(q + 1, q + 1 + m, cmpid);for(i = 1; i <= m; i++)cout << q[i].a << "/" << q[i].b) << endl;return 0;
}

带修莫队

分块大小 e l o g ( q u e r y ) + l o g ( c h a n g e ) 3 e^{\frac{log(query) + log(change)}{3}} e3log(query)+log(change)​

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr)
#define rint register int
typedef long long LL;
const int maxn = 1e6 + 5;int a[maxn], k[maxn], cnt[maxn];
int ans[maxn], now;struct node{int l, r, t, id;inline bool operator < (const node& b)const{return k[l] == k[b.l] ? (k[r] == k[b.r] ? t < b.t : r < b.r) : l < b.l;}
}q[maxn], c[maxn];inline void add(rint x){cnt[x]++;if(cnt[x] == 1) now++;
}inline void del(rint x){cnt[x]--;if(!cnt[x]) now--;
}int main(){IOS;rint n, m, sq, qcnt = 0, ccnt = 0;char flag;cin >> n >> m;for(rint i = 1; i <= n; i++) cin >> a[i];for(rint i = 1; i <= m; i++){cin >> flag;if(flag == 'Q'){++qcnt;cin >> q[qcnt].l >> q[qcnt].r;q[qcnt].id = qcnt;q[qcnt].t = i;} else{++ccnt;cin >> c[ccnt].l >> c[ccnt].r;c[ccnt].t = i;}}c[ccnt + 1].t = 1e9;sort(q + 1, q + 1 + qcnt);sq = ceil(exp((log(qcnt + 1) + log(ccnt + 1)) / 3));
//    sq = sqrt(n);for(rint i = 1; i <= n; i++) k[i] = i / sq;sort(q + 1, q + 1 + qcnt);rint l = 1, r = 0, p = 0;for(rint i = 1; i <= qcnt; i++){while(r < q[i].r) add(a[++r]);while(r > q[i].r) del(a[r--]);while(l < q[i].l) del(a[l++]);while(l > q[i].l) add(a[--l]);while(c[p + 1].t < q[i].t){p++;if(q[i].l <= c[p].l && c[p].l <= q[i].r){add(c[p].r);del(a[c[p].l]);}swap(c[p].r, a[c[p].l]);}while(c[p].t > q[i].t){if(q[i].l <= c[p].l && c[p].l <= q[i].r){add(c[p].r);del(a[c[p].l]);}swap(c[p].r, a[c[p].l]);p--;}ans[q[i].id] = now;}for(rint i = 1; i <= qcnt; i++) cout << ans[i] << endl;return 0;
}

带修莫队取MEX

//CF940F Machine Learning
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr)
#define rint register int
typedef long long LL;
const int maxn = 2e5 + 5;int a[maxn], sa[maxn], k[maxn], cnt[maxn], mex[maxn];
int ans[maxn], now = 1;struct node{int l, r, t, id;inline bool operator < (const node& b)const{return k[l] == k[b.l] ? (k[r] == k[b.r] ? t < b.t : r < b.r) : l < b.l;}
}q[maxn], c[maxn];inline void add(rint x){if(cnt[x] >= 1){  // maybe <= 0mex[cnt[x]]--;if(!mex[cnt[x]]) now = min(now, cnt[x]);}mex[++cnt[x]]++;while(mex[now]) now++;
}inline void del(rint x){if(cnt[x] >= 1){   // maybe <= 0mex[cnt[x]]--;if(!mex[cnt[x]]) now = min(now, cnt[x]);}mex[--cnt[x]]++;while(mex[now]) now++;
}int main(){IOS;rint n, m, sq, qcnt = 0, ccnt = 0, sacnt = 0, opt;cin >> n >> m;for(rint i = 1; i <= n; i++){cin >> a[i];sa[++sacnt] = a[i];}for(rint i = 1; i <= m; i++){cin >> opt;if(opt == 1){++qcnt;cin >> q[qcnt].l >> q[qcnt].r;q[qcnt].id = qcnt;q[qcnt].t = i;} else{++ccnt;cin >> c[ccnt].l >> c[ccnt].r;sa[++sacnt] = c[ccnt].r;c[ccnt].t = i;}}sort(sa + 1, sa + 1 + sacnt);for(int i = 1; i <= n; i++) a[i] = lower_bound(sa + 1, sa + 1 + sacnt, a[i]) - sa;for(int i = 1; i <= ccnt; i++) c[i].r = lower_bound(sa + 1, sa + 1 + sacnt, c[i].r) - sa;c[ccnt + 1].t = 1e9;sort(q + 1, q + 1 + qcnt);sq = ceil(exp((log(qcnt + 1) + log(ccnt + 1)) / 3));for(rint i = 1; i <= n; i++) k[i] = i / sq;sort(q + 1, q + 1 + qcnt);rint l = 1, r = 0, p = 0;for(rint i = 1; i <= qcnt; i++){while(r < q[i].r) add(a[++r]);while(r > q[i].r) del(a[r--]);while(l < q[i].l) del(a[l++]);while(l > q[i].l) add(a[--l]);while(c[p + 1].t < q[i].t){p++;if(q[i].l <= c[p].l && c[p].l <= q[i].r){add(c[p].r);del(a[c[p].l]);}swap(c[p].r, a[c[p].l]);}while(c[p].t > q[i].t){if(q[i].l <= c[p].l && c[p].l <= q[i].r){add(c[p].r);del(a[c[p].l]);}swap(c[p].r, a[c[p].l]);p--;}ans[q[i].id] = now;}for(rint i = 1; i <= qcnt; i++) cout << ans[i] << endl;return 0;
}

单调增加莫队(回滚莫队&不删除莫队)

适用于删除维护麻烦,而增加维护容易的题目。

以块作为单位。我们知道莫队满足,左端位于同一个块中时,右端会单调递增。

我们按块来枚举,枚举左端位于同一个块的所有查询。有以下两种情况:

当左端和右端位于同一个块中。暴力算,单次计算复杂度 O ( n ) O(\sqrt{n}) O(n ​),最多 n ∗ O ( n ) n*O(\sqrt{n}) n∗O(n ​)

当左端和右端不位于同一个块中,右端单调递增。同一个块中的右端计算总复杂度 O ( n ) O(n) O(n),最多 n \sqrt{n} n ​ 个块,即 n ∗ O ( n ) \sqrt{n}*O(n) n ​∗O(n)。

当左端和右端不在同一个块中,每次重新暴力算左端,单次复杂度 O ( n ) O(\sqrt{n}) O(n ​),最多 n ∗ O ( n ) n*O(\sqrt{n}) n∗O(n ​)

综上,时间复杂度为 O ( n ∗ n ) O(n*\sqrt{n}) O(n∗n ​),标准莫队复杂度。

//P5906 【模板】回滚莫队&不删除莫队
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr)
#define rint register int
typedef long long LL;
const int maxn = 2e5 + 5;int a[maxn], sa[maxn], k[maxn], cl[maxn];
int ans[maxn], first[maxn], le[maxn], ri[maxn];struct node{int l, r, id;inline bool operator < (const node& b)const{return k[l] == k[b.l] ? r < b.r : l < b.l;}
}q[maxn];inline int read(){rint x = 0;char c = getchar();while(c < '0') c = getchar();while(c >= '0'){x = (x << 1) + (x << 3) + c - 48;c = getchar();}return x;
}inline int cal(rint l, rint r){rint res = 0;for(rint i = l; i <= r; i++) first[a[i]] = 0;for(rint i = l; i <= r; i++){if(first[a[i]]) res = max(res, i - first[a[i]]);else first[a[i]] = i;}return res;
}int main(){IOS;rint n, m, sq, lens;n = read();sq = sqrt(n);lens = (n + sq - 1) / sq;       // the cnt of all blocksfor(rint i = 1; i <= n; i++){a[i] = read();sa[i] = a[i];}sort(sa + 1, sa + 1 + n);for(rint i = 1; i <= n; i++) a[i] = lower_bound(sa + 1, sa + 1 + n, a[i]) - sa;m = read();for(rint i = 1; i <= m; i++){q[i].l = read(), q[i].r = read();q[i].id = i;}for(rint i = 1; i <= n; i++) k[i] = (i - 1) / sq + 1;        // the idx of blocksort(q + 1, q + 1 + m);for(rint i = 1, j = 1; j <= lens; j++){rint br = min(n, j * sq), l = br + 1, r = br, now = 0, cnt = 0;for(; k[q[i].l] == j; i++){         // using the same block history messageif(k[q[i].r] == j) ans[q[i].id] = cal(q[i].l, q[i].r);  // O(sq), most n * O(sq)else{while(r < q[i].r){                          // O(n), most sq * O(n)++r;ri[a[r]] = r;if(!le[a[r]]){le[a[r]] = r;cl[++cnt] = a[r];}now = max(now, r - le[a[r]]);}int save = now;while(l > q[i].l){                          // O(sq), most n * O(sq)--l;if(ri[a[l]]) now = max(now, ri[a[l]] - l);else ri[a[l]] = l;}ans[q[i].id] = now;now = save;while(l <= br){                             // clear the block message, O(sq), most n * O(sq)if(ri[a[l]] == l) ri[a[l]] = 0;l++;}}}while(cnt){                                         // clear the block message, O(n), most sq * O(n)ri[cl[cnt]] = le[cl[cnt]] = 0;cnt--;}}for(rint i = 1; i <= m; i++){cout << ans[i] << endl;}return 0;
}

树上莫队

利用 d f s dfs dfs 序,记录每个点的进入时间戳和返回时间戳。

i d x idx idx,时间戳。 s t st st,进入时间戳。 e d ed ed,返回时间戳。

然后将树上查询查询,变为时间戳上的查询。

我们始终认为 s t [ u ] < = s t [ v ] st[u]<=st[v] st[u]<=st[v]。

当 u = l c a ( u , v ) u=lca(u,v) u=lca(u,v) 时,直接统计 s t [ u ] st[u] st[u] 到 s t [ v ] st[v] st[v] 上只出现一次的点。

当 u ≠ l c a ( u , v ) u\neq lca(u,v) u​=lca(u,v) 时。统计 e d [ u ] ed[u] ed[u] 到 s t [ v ] st[v] st[v] 的时间戳上只出现一次的点。同时统计上 l c a ( u , v ) lca(u,v) lca(u,v)(因为不在该时间戳序列中,但是在他们的路径上)。

为什么是只统计出现一次的点。因为出现两次(递归返回)或零次(压根没进入)都说明这个点不在路径上。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr)
#define rint register int
typedef long long LL;
const int maxn = 1e5 + 5;
// idx double memory
int k[maxn], a[maxn], sa[maxn], cnt[maxn], vis[maxn], ans[maxn];
int idx[maxn], ed[maxn], st[maxn], d[maxn], f[maxn][18], tol, now;vector<int> edge[maxn];struct node{int l, r, id, add;inline bool operator < (const node &s) const{return k[l] == k[s.l] ? r < s.r : l < s.l;}
}q[maxn];inline void dfs(rint u, rint fa, rint dep){d[u] = dep;f[u][0] = fa;for(rint i = 1; i < 18; i++) f[u][i] = f[f[u][i - 1]][i - 1];idx[++tol] = u;st[u] = tol;for(int v : edge[u]){if(v == fa) continue;dfs(v, u, dep + 1);}idx[++tol] = u;ed[u] = tol;
}inline int LCA(rint x, rint y){if(d[x] > d[y]) swap(x, y);rint dep = d[y] - d[x];for(rint i = 0; i < 18; i++){if(dep & (1 << i)) y = f[y][i];}for(rint i = 17; ~i; i--){if(f[x][i] != f[y][i]){x = f[x][i];y = f[y][i];}}return x == y ? x : f[y][0];
}inline int read(){rint x = 0;char c = getchar();while(c < '0') c = getchar();while(c >= '0'){x = (x << 1) + (x << 3) + c - 48;c = getchar();}return x;
}inline void add(rint x){vis[x]++;if(vis[x] == 1){cnt[a[x]]++;if(cnt[a[x]] == 1) now++;}else if(vis[x] == 2){          // 2cnt[a[x]]--;if(!cnt[a[x]]) now--;}
}inline void del(rint x){vis[x]--;if(vis[x] == 1){cnt[a[x]]++;if(cnt[a[x]] == 1) now++;}else if(vis[x] == 0){          // 0cnt[a[x]]--;if(!cnt[a[x]]) now--;}
}int main(){IOS;rint n, m, u, v, lca, sq;n = read(), m = read();for(int i = 1; i <= n; i++){a[i] = read();sa[i] = a[i];}for(rint i = 1; i < n; i++){u = read(), v = read();edge[u].push_back(v);edge[v].push_back(u);}sort(sa + 1, sa + 1 + n);for(rint i = 1; i <= n; i++) a[i] = lower_bound(sa + 1, sa + 1 + n, a[i]) - sa;dfs(1, 1, 1);for(rint i = 1; i <= m; i++){u = read(), v = read();if(st[u] > st[v]) swap(u, v);lca = LCA(u, v);if(lca == u) q[i] = node{st[u], st[v], i, 0};else q[i] = node{ed[u], st[v], i, a[lca]};}sq = sqrt(tol);for(rint i = 1; i <= tol; i++) k[i] = (i - 1) / sq + 1;sort(q + 1, q + 1 + m);rint l = 1, r = 0;for(rint i = 1; i <= m; i++){while(r < q[i].r) add(idx[++r]);while(r > q[i].r) del(idx[r--]);while(l < q[i].l) del(idx[l++]);while(l > q[i].l) add(idx[--l]);ans[q[i].id] = now;if(q[i].add && !cnt[q[i].add]) ans[q[i].id]++;}for(rint i = 1; i <= m; i++) cout << ans[i] << endl;return 0;
}

树上带修莫队

跟树上莫队思路类似,利用 d f s dfs dfs 序将树转为序列。

将树上查询改为序列查询。

而带修的时间复杂度则与带修莫队相同, 3 n 4 t ^3\sqrt{n^4t} 3n4t ​

其他操作仿写即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr)
#define rint register int
typedef long long LL;
const int maxn = 1e6 + 5;
// idx double memoryvector<int> edge[maxn];
int v[maxn], w[maxn], a[maxn], vis[maxn], cnt[maxn];
int k[maxn << 1], fa[maxn][20], d[maxn], idx[maxn << 1], st[maxn], ed[maxn];
int tol;
LL ans[maxn], now;struct node{int l, r, t, add, id;inline bool operator < (const node &b) const{return k[l] == k[b.l] ? (k[r] == k[b.r] ? t < b.t : r < b.r) : l < b.l;}
}query[maxn], change[maxn];inline int read(){rint x = 0;char c = getchar();while(c < '0') c = getchar();while(c >= '0'){x = (x << 1) + (x << 3) + c - 48;c = getchar();}return x;
}inline void dfs(rint u, rint f, rint dep){fa[u][0] = f;for(rint i = 1; i < 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];d[u] = dep;idx[++tol] = u;st[u] = tol;for(int s : edge[u]){if(s == f) continue;dfs(s, u, dep + 1);}idx[++tol] = u;ed[u] = tol;
}inline int LCA(rint x, rint y){if(d[x] > d[y]) swap(x, y);rint dep = d[y] - d[x];for(rint i = 0; i < 20; i++){if(dep & (1 << i)) y = fa[y][i];}for(rint i = 19; ~i; i--){if(fa[x][i] != fa[y][i]){x = fa[x][i];y = fa[y][i];}}return x == y ? x : fa[y][0];
}inline void add(rint x){vis[x]++;if(vis[x] == 1){x = a[x];now += 1LL * w[++cnt[x]] * v[x];}else if(vis[x] == 2){x = a[x];now -= 1LL * w[cnt[x]--] * v[x];}
}inline void del(rint x){vis[x]--;if(vis[x] == 1){x = a[x];now += 1LL * w[++cnt[x]] * v[x];}else if(vis[x] == 0){x = a[x];now -= 1LL * w[cnt[x]--] * v[x];}
}int main(){IOS;rint n, m, q, x, y, qcnt = 0, ccnt = 0, opt, l, r, sq, lca;n = read(), m = read(), q = read();for(rint i = 1; i <= m; i++) v[i] = read();for(rint i = 1; i <= n; i++) w[i] = read();for(rint i = 1; i < n; i++){x = read(), y = read();edge[x].push_back(y);edge[y].push_back(x);}dfs(1, 1, 1);for(rint i = 1; i <= n; i++) a[i] = read();     // id of vfor(rint i = 1; i <= q; i++){opt = read(), l = read(), r = read();if(opt){++qcnt;if(st[l] > st[r]) swap(l, r);lca = LCA(l, r);if(lca == l){query[qcnt] = node{st[l], st[r], i, 0, qcnt};}else{query[qcnt] = node{ed[l], st[r], i, lca, qcnt};}}else change[++ccnt] = node{l, r, i, 0, 0};}change[ccnt + 1].t = 1e9;sq = ceil(exp((log(qcnt + 1) + log(ccnt + 1)) / 3));for(rint i = 1; i <= tol; i++) k[i] = (i - 1) / sq + 1;sort(query + 1, query + 1 + qcnt);rint p = 0;l = 1, r = 0;for(rint i = 1; i <= qcnt; i++){while(r < query[i].r) add(idx[++r]);    // pwhile(r > query[i].r) del(idx[r--]);while(l < query[i].l) del(idx[l++]);while(l > query[i].l) add(idx[--l]);while(change[p + 1].t < query[i].t){++p;if(vis[change[p].l] == 1){x = a[change[p].l], y = change[p].r;now -= 1LL * w[cnt[x]--] * v[x];now += 1LL * w[++cnt[y]] * v[y];}swap(change[p].r, a[change[p].l]);}while(change[p].t > query[i].t){if(vis[change[p].l] == 1){x = a[change[p].l], y = change[p].r;now -= 1LL * w[cnt[x]--] * v[x];now += 1LL * w[++cnt[y]] * v[y];}swap(change[p].r, a[change[p].l]);p--;}ans[query[i].id] = now;if(query[i].add){x = a[query[i].add];ans[query[i].id] += 1LL * w[cnt[x] + 1] * v[x];}}for(rint i = 1; i <= qcnt; i++) cout << ans[i] << endl;return 0;
}

启发式搜索(玄学)

模拟退火

// 说实话这种玄学东西,我是挺讨厌的
// 洛谷 P1337
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int maxn = 1e3 + 1;
double randrand(){return (double)rand()/RAND_MAX;}
double coor[maxn][10], w[maxn], LR[10][3];
int n;
double f(double x, double y){double sumx = 0, sumy = 0, d;for(int i = 1; i <= n; i++){d = sqrt((x - coor[i][1])*(x - coor[i][1]) + (y - coor[i][2])*(y - coor[i][2]));if(d == 0)continue;sumx += (coor[i][1] - x)/d*w[i];sumy += (coor[i][2] - y)/d*w[i];}return fabs(sumx) + fabs(sumy);
}
void sa(double &x, double &y, double &ans){double t = 3000, eps = 1e-15;while(t > eps){double xtemp = x + (2*rand() - RAND_MAX)*t;double ytemp = y + (2*rand() - RAND_MAX)*t;if(xtemp < LR[1][1] || xtemp > LR[1][2])xtemp = LR[1][1] + randrand()*(LR[1][2] - LR[1][1]);if(ytemp < LR[2][1] || ytemp > LR[2][2])ytemp = LR[2][1] + randrand()*(LR[2][2] - LR[2][1]);double now = f(xtemp,ytemp);if(now < ans){ans = now;x = xtemp;y = ytemp;}else if(exp((ans-now)/t) > randrand()){x = xtemp;y = ytemp;}t *= 0.98;}
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cout << fixed << setprecision(3);cin >> n;for(int i = 1; i <= 2; i++)LR[i][1] = 10000, LR[i][2] = -10000;for(int i = 1; i <= n; i++){for(int j = 1; j <= 2; j++){cin >> coor[i][j];LR[j][1] = min(LR[j][1], coor[i][j]);LR[j][2] = max(LR[j][2], coor[i][j]);}cin >> w[i];}double x = LR[1][1] + randrand()*(LR[1][2] - LR[1][1]), y = LR[2][1] + randrand()*(LR[2][2] - LR[2][1]);double ans = f(x,y);sa(x, y, ans);cout << x << " " << y << endl;return 0;}

差分进化算法

// 洛谷 p1337
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<iomanip>
#include<time.h>
#include<math.h>
using namespace std;
#define endl '\n'
int n,ra[10],tol=2,np=20;   //tol是函数的自变量数量,np是种群总数,10*自变量数量
double xl[11],xr[11],cf=0.5,cr=0.5;  //xl,xr函数边界;double randrand(){return (double)rand()/RAND_MAX;}struct point{double x[11],w;void add(point &a){for(int i=1;i<=tol;i++)x[i]+=a.x[i];}void sub(point &a){for(int i=1;i<=tol;i++)x[i]-=a.x[i];}void mul(double c){for(int i=1;i<=tol;i++)x[i]*=c;}void cpy(point &a){for(int i=1;i<=tol;i++)x[i]=a.x[i];}double disto(point &a){double sum=0;for(int i=1;i<=tol;i++)sum+=(x[i]-a.x[i])*(x[i]-a.x[i]);return sqrt(sum);}void random(){for(int i=1;i<=tol;i++)x[i]=xl[i]+randrand()*(xr[i]-xl[i]);}bool isover(){for(int i=1;i<=tol;i++)if(x[i]<xl[i]||x[i]>xr[i])return true;return false;}
}po[1005],an[40],van[40];double f(point &a){double sumx=0,sumy=0,temp;for(int i=1;i<=n;i++){temp=a.disto(po[i]);if(temp==0)continue;sumx+=(po[i].x[1]-a.x[1])/temp*po[i].w;sumy+=(po[i].x[2]-a.x[2])/temp*po[i].w;}return fabs(sumx)+fabs(sumy);
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);double x[11],ans,ansx[11];cout<<fixed<<setprecision(3);cin>>n;for(int i=1;i<=tol;i++)xl[i]=10000,xr[i]=-10000;for(int i=1;i<=n;i++){for(int j=1;j<=tol;j++){cin>>po[i].x[j];xl[j]=min(xl[j],po[i].x[j]);xr[j]=max(xr[j],po[i].x[j]);}cin>>po[i].w;}for(int i=1;i<=np;i++)an[i].random();ans=f(an[1]);for(int i=1;i<=tol;i++)ansx[i]=an[1].x[i];double oldone,newone;int flag;for(int tt=1;tt<=300&&ans>0.00001;tt++){for(int i=1;i<=np;i++){for(int j=0;j<=2;j++)ra[j]=(int)round(randrand()*(np-1))+1;van[i].cpy(an[ra[1]]);van[i].sub(an[ra[2]]);van[i].mul(cf*pow(2,exp(1.0-(double)tt/(303-tt))));van[i].add(an[ra[0]]);if(van[i].isover())van[i].random();}for(int i=1;i<=np;i++){flag=(int)round(randrand()*1)+1;for(int j=1;j<=2;j++)if(flag==j)continue;else if(randrand()>cr)van[i].x[j]=an[i].x[j];}for(int i=1;i<=np;i++){newone=f(van[i]),oldone=f(an[i]);if(newone<oldone){oldone=newone;an[i].cpy(van[i]);}if(oldone<ans){ans=oldone;for(int j=1;j<=tol;j++)ansx[j]=an[i].x[j];}}}cout<<ansx[1]<<" "<<ansx[2]<<endl;return 0;
}

A*算法 (启发式 BFS)

// P1379 八数码难题
#include <bits/stdc++.h>
using namespace std;struct node{string state;int gn, hn, id;int x, y;   // point of zerobool operator < (const node&a) const{return gn + hn == a.gn + a.hn ? gn > a.gn : gn + hn > a.gn + a.hn;}
};const string target = "123804765";
int cnt;
int mx[4] = {1, 0, -1, 0};
int my[4] = {0, -1, 0, 1};vector<int> fa;
set<string> state_history;
priority_queue<node> open_set;
map<pair<int, int>, int> pos;
map<int, pair<int, int> > re_pos;inline int cal_hn(const string &s){int res = 0;for(int i = 0; i < s.size(); i++){for(int j = 0; j < target.size(); j++){if(s[i] == target[j]){res += abs(re_pos[i].first - re_pos[j].first) + abs(re_pos[i].second - re_pos[j].second);}}}return res;
}int main(){for(int i = 0; i < 3; i++){for(int j = 0; j < 3; j++){pos.insert({{i, j}, j * 3 + i});re_pos.insert({j * 3 + i, {i, j}});}}int ans = 0, x = -1, y = -1;node tmp;string first_state;cin >> first_state;for(int i = 0; i < 3; i++){for(int j = 0; j < 3; j++){if(first_state[j * 3 + i] == '0'){x = i, y = j;break;}}if(~x) break;}tmp = node{first_state, 0, 0, 0, x, y};open_set.push(tmp);fa.push_back(-1);vector<int> res;while(!open_set.empty()){tmp = open_set.top();open_set.pop();if(tmp.state == target){res.push_back(tmp.id);int id = fa[tmp.id];while(~id){res.push_back(id);id = fa[id];ans++;}break;}else{for(int i = 0; i < 4; i++){int tx = tmp.x + mx[i], ty = tmp.y + my[i];if(tx < 0 || tx > 2 || ty < 0 || ty > 2) continue;string now_state = tmp.state;swap(now_state[pos[{tmp.x, tmp.y}]], now_state[pos[{tx, ty}]]);if(state_history.count(now_state)) continue;state_history.insert(now_state);node now = node{now_state, tmp.gn + 1,  cal_hn(now_state), ++cnt, tx, ty};fa.push_back(tmp.id);open_set.push(now);}}}cout << ans << endl;return 0;
}

IDA* 算法(启发式DFS)

// P1379 八数码难题
#include <bits/stdc++.h>
using namespace std;const char target[3][3] = {'1', '2', '3', '8', '0', '4', '7', '6', '5'};
int target_x[9] = {1, 0, 0, 0, 1, 2, 2, 2, 1};
int target_y[9] = {1, 0, 1, 2, 2, 2, 1, 0, 0};char now[3][3];
int mx[4] = {1, 0, -1, 0};
int my[4] = {0, -1, 0, 1};
int limit;inline int cal_hn(){int res = 0;for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {res += abs(i - target_x[now[i][j] - '0']) + abs(j - target_y[now[i][j] - '0']);}}return res;
}int dfs(int dep, int x, int y, int lx, int ly){int hn = cal_hn();if (dep + hn > limit) return -1;if (!hn) return dep;for(int i = 0; i < 4; i++){int tx = x + mx[i], ty = y + my[i];if(tx < 0 || tx > 2 || ty < 0 || ty > 2) continue;if(tx == lx && ty == ly) continue;swap(now[x][y], now[tx][ty]);int res = dfs(dep + 1, tx, ty, x, y);if(~res) return res;swap(now[x][y], now[tx][ty]);}return -1;
}int main(){for (auto & i : now) {for (char & j : i) {cin >> j;}}int x, y;for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (now[i][j] == '0'){x = i;y = j;break;}}}while(++limit){int res = dfs(0, x, y, x, y);if (~res){cout << res << endl;break;}}return 0;
}
/**/

自备ACM模板 —— 其他技巧相关推荐

  1. 自备ACM模板 —— 数据结构篇

    文章目录 ST表 线段树 区间加.区间乘混合 主席树 主席树+差分 主席树查询区间前K项前缀和 带修主席树 树上主席树 带修树上主席树 主席树判断存在性问题 二分套主席树 区间修改+标记永久化+树状数 ...

  2. 我的所有优质博客全部开源啦(我自己原创的《ACM模板》《算法全家桶》《算法竞赛中的初等数论》 PDF免费下载)

    你好呀ヾ(≧▽≦*)o 我是繁凡さん 这两年来我写了很多长篇文章,主要涉及数据结构,算法,程序设计竞赛,数学,计算几何等方面的内容: <数据结构>C语言版(清华严蔚敏考研版) 全书知识梳理 ...

  3. ACM模板(满注释模板!)

    转自:https://fanfansann.blog.csdn.net/article/details/105493218 目录 F o r e w o r d ForewordForeword T ...

  4. 奇技淫巧-STL 库 ACM算法小技巧(持续更新中~~~)

    STL 库中的奇技淫巧 STL 是惠普实验室开发的一系列软件的统称,可以理解为一些容器的集合.STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件.STL 现在是C++的一部分,因此不用额 ...

  5. ACM模板 | 学习笔记 树相关

    持续更新中qwq 咕咕咕 此次update是在我原先自己的博客园博客的基础上进行更新的(隔了两年该忘的不该忘的都忘完了qwq),顺便整理一下我的acm模板QAQ (我保证2021.3.1开学之前搞完! ...

  6. 刘晓艳的作文笔记 自己构造模板 写作技巧 同义替换

    本文内容为刘晓艳的作文笔记 自己构造模板 写作技巧 同义替换,源文件已经上传到我的资源中,有需要的可以去看看, 我主页中的思维导图中内容大多从我的笔记中整理而来,相应技巧可在笔记中查找原题, 有兴趣的 ...

  7. 线性求逆元模板_ZXBlog/ACM模板(C++).md at bb6f2522054d5370df79222461293721e8edede2 · cw1027/ZXBlog · GitHub...

    ACM模板(C++) 1.大数 加法,乘法模板 //题目链接 : http://poj.org/problem?id=2506 //题目大意 : 就是问你用2*1,1*2,2*2的砖拼成2*n的长方形 ...

  8. phpcms v9 模板标签技巧,模板标签常用方法

    phpcms v9 模板标签技巧,模板标签常用方法 phpcms v9模板制作常用代码集合 1.截取调用标题长度 {str_cut($r[title],36,'')} 2.格式化时间 调用格式化时间 ...

  9. ACM 算法竞赛入门级模板 ------ (比赛技巧工具)

    1.文本输入.输出 void fre(){freopen("C:\\Users\\acm\\Desktop\\输入文本.txt", "r", stdin);fr ...

最新文章

  1. 2015大型互联网公司校招都开始了,薪资你准备好了嘛?
  2. matlab过滤,matlab过滤问题
  3. FFMPEG使用参数详解
  4. Costco的中国门徒已经参透了零售成功秘笈
  5. linux主机查看CPU核数和主机型号
  6. zynq开发系列6:创建AXI IP实现PS对PL的数据配置(步骤三配置SDK)
  7. java线程下载文件_使用多线程在Java下载文件
  8. Android基础控件TextClock和Chronometer的使用
  9. 【转】用户空间编写驱动程序
  10. 海康DS-2DY5223IW-DM
  11. 在Mac电脑中配置ios模拟器
  12. js模板引擎template.js的使用
  13. 安装谷歌服务框架2022最新版本22.45.15失败
  14. cxf配置http:conduit
  15. 开关控制灯实验C语言编程,指示灯开关控制器实验.doc
  16. 【数据分析师自学系列-MySQL】创建新表create table、create table as、create table like的区别
  17. 线束音视频传输连接器FAKRA与HSD区别?
  18. 一文告诉你什么叫边缘计算
  19. g++来进行编译的头文件路径问题
  20. 02华为大数据HCIE_Data Mining 数学基础 测试一下

热门文章

  1. ec11编码器c语言程序,单片机驱动EC11编码器源程序
  2. 高薪行业2016年调薪预估率出炉
  3. (javascript)parent的用法
  4. 题目24 身高、体重排序(ok)
  5. 3d效果图制作傻瓜软件_除了3DMax,还有什么软件可以快捷做出室内效果图?
  6. html5 模拟人生,PS日服2月会免:《模拟人生4》《防火墙:绝命时刻》
  7. 众昂矿业:萤石在战略性新兴产业中的地位和作用
  8. 创客匠人助力“音体美”培训机构布局线上
  9. 公安部科信局主管杂志《中国安防》深度专访欧科云链副总裁张超
  10. Java技术体系大全,准备面试的可以参考一下!