文章目录
树上倍增
求 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.c
temp[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.c
temp[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 <= 0
mex[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 <= 0
mex[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 blocks
for(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 block
sort(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 message
if(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){ // 2
cnt[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){ // 0
cnt[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 memory
vector<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 v
for(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]); // p
while(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 zero
bool 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;
}
/*
*/