@ljt12138
2017-05-28T10:17:56.000000Z
字数 13025
阅读 954
学习了一个斜堆的写法,非常好写,于是又去切了可并堆模板[Apio2012]dispatching:
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct lheap {long long dat;int lc, rc;lheap() { lc = rc = 0; }} tree[MAXN*2];int tp = 0;int merge(int a, int b){if (a == 0 || b == 0) return a+b;if (tree[a].dat < tree[b].dat) swap(a, b);swap(tree[a].lc, tree[a].rc);return tree[a].rc = merge(tree[a].rc, b), a;}long long pop(int &a){long long ret = tree[a].dat;a = merge(tree[a].lc, tree[a].rc);return ret;}struct node {int to, next;} edge[MAXN];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int n;long long m, ans = 0;int b[MAXN];long long c[MAXN], l[MAXN];int rt;int dfs(int nd, long long &sig, int &cnt){int tr = ++tp;long long sum; int ct;sig = tree[tr].dat = c[nd], cnt = 1;for (int i = head[nd]; i; i = edge[i].next)tr = merge(tr, dfs(edge[i].to, sum, ct)), sig += sum, cnt += ct;while (sig > m) sig -= pop(tr), cnt--;ans = max(ans, cnt*l[nd]);return tr;}int main(){scanf("%d%lld", &n, &m);for (int i = 1; i <= n; i++) {scanf("%d%lld%lld", &b[i], &c[i], &l[i]);if (b[i]) push(b[i], i);else rt = i;}long long t;int cnt;dfs(rt, t, cnt);printf("%lld\n", ans);return 0;}
结论题/找规律题,递归求解发现应该交替插入,然后只要分类讨论四种情况就好。
#include <bits/stdc++.h>using namespace std;const int MAXN = 105;int lc[MAXN], rc[MAXN], n;int solve(int nd, int a[], int &top){top = 0;int l[MAXN], r[MAXN], ltop = 0, rtop = 0, lf = 0, rf = 0;if (lc[nd] != -1) solve(lc[nd], l, ltop);if (rc[nd] != -1) solve(rc[nd], r, rtop);if (ltop-rtop > 1) {while (ltop-lf > rtop-rf) a[++top] = l[++lf];a[++top] = nd;while (ltop-lf > 0) a[++top] = r[++rf], a[++top] = l[++lf];} else if (ltop == rtop+1) {a[++top] = nd;while (ltop-lf > 0) {a[++top] = l[++lf];if (rtop-rf > 0) a[++top] = r[++rf];}} else if (ltop == rtop) {a[++top] = nd;while (rtop-rf > 0)a[++top] = r[++rf], a[++top] = l[++lf];} else {while (rtop-rf > ltop-lf-1) a[++top] = r[++rf];a[++top] = nd;while (ltop-lf > 0) {a[++top] = l[++lf];if (rtop-rf > 0) a[++top] = r[++rf];}}}int main(){int a[MAXN], u;memset(lc, -1, sizeof lc);memset(rc, -1, sizeof rc);scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &u);if (u < 100) lc[u] = i;else rc[u-100] = i;}solve(0, a, u);for (int i = 1; i <= u; i++)printf("%d ", a[i]);return 0;}
比较有意思的数据结构题。大概就是用可并堆维护还存活着的骑士,然后只需要lazy_tag就好了。
注意两种lazy_tag一定要考虑彼此的影响!
#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;struct sheap {pair<long long, int> dat;int lc, rc;long long tag1, tag2;sheap() { tag1 = 0, tag2 = 1; }} tree[MAXN*2];int tp = 0;long long a[MAXN], b[MAXN];int n, m;long long h[MAXN];long long s[MAXN];int c[MAXN], u;vector<int> v[MAXN];inline void pdw(int nd){if (!nd) return;tree[nd].dat.first *= tree[nd].tag2;if (tree[nd].lc)tree[tree[nd].lc].tag2 *= tree[nd].tag2, tree[tree[nd].lc].tag1 *= tree[nd].tag2;if (tree[nd].rc)tree[tree[nd].rc].tag2 *= tree[nd].tag2, tree[tree[nd].rc].tag1 *= tree[nd].tag2;tree[nd].dat.first += tree[nd].tag1;if (tree[nd].lc) tree[tree[nd].lc].tag1 += tree[nd].tag1;if (tree[nd].rc) tree[tree[nd].rc].tag1 += tree[nd].tag1;tree[nd].tag1 = 0, tree[nd].tag2 = 1;}inline void mul_dt(int nd, long long dt){ if (!nd) return; tree[nd].tag2 *= dt, tree[nd].tag1 *= dt; }inline void plus_dt(int nd, long long dt){ if (!nd) return; tree[nd].tag1 += dt; }void tour(int nd, int tab = 0){if (!nd) return;pdw(nd);tour(tree[nd].lc, tab+2), tour(tree[nd].rc, tab+2);}int merge(int a, int b){if (a == b && a != 0) throw;pdw(a), pdw(b);if (a == 0 || b == 0) return a+b;if (tree[a].dat.first > tree[b].dat.first) swap(a, b);swap(tree[a].lc, tree[a].rc);return tree[a].lc = merge(tree[a].lc, b), a;}inline long long top_ele(int a){ return pdw(a), tree[a].dat.first; }inline void pop(int &a){pdw(a);a = merge(tree[a].lc, tree[a].rc);}struct node {int to, next;} edge[MAXN];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int cnt[MAXN], depth[MAXN], death[MAXN];int dfs(int nd){int hp = 0;for (int i = 0; i < v[nd].size(); i++) {hp = merge(hp, (tree[++tp].dat = make_pair(s[v[nd][i]], v[nd][i]), tp));}for (int i = head[nd]; i; i = edge[i].next) {depth[edge[i].to] = depth[nd]+1;hp = merge(hp, dfs(edge[i].to));}while (hp && top_ele(hp) < h[nd]) {pair<long long, int> dat = tree[hp].dat;death[dat.second] = nd, cnt[nd]++;pop(hp);}if (!hp) return hp;if (a[nd] == 0) plus_dt(hp, b[nd]);else mul_dt(hp, b[nd]);return hp;}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) scanf("%lld", &h[i]);for (int i = 2; i <= n; i++)scanf("%d%lld%lld", &u, &a[i], &b[i]), push(u, i);for (int i = 1; i <= m; i++){scanf("%lld%d", &s[i], &c[i]);v[c[i]].push_back(i);}depth[1] = 1;dfs(1);for (int i = 1; i <= n; i++) printf("%d\n", cnt[i]);for (int i = 1; i <= m; i++) printf("%d\n", depth[c[i]]-depth[death[i]]);return 0;}
类似约瑟夫问题,树状数组上二分小trick,貌似一年前就在千古神犇lcy的课上学习过。就是二进制一位一位确定。
#include <bits/stdc++.h>using namespace std;const int MAXN = 700005;inline int lowbit(int i) { return i&(-i); }int c[MAXN], n;void modify(int pos, int dt){ for (int i = pos; i <= n; i += lowbit(i)) c[i] += dt; }int sum(int k){int now = 0, dat = 0;for (int i = 1<<25; i; i >>= 1) {if (now+i <= n && dat+c[i+now] < k)dat += c[i+now], now += i;}return now+1;}int query(int a){int ans = 0;for (int i = a; i; i -= lowbit(i))ans += c[i];return ans;}int main(){scanf("%d", &n);for (int i = 1; i <= n; i++) modify(i, 1);int now = 0;for (int i = 1; i <= n; i++) {int last = n-i+1, k;scanf("%d", &k);k += query(now) + 1;int p = k%last;if (p == 0) p = last;now = sum(p), modify(now, -1);printf("%d\n", now);}return 0;}
出题人大概没有学过高中数学...明明是均分之间的区间。
一开始还以为需要玄学可持久化,后来发现只需要在合适的地方打个表,然后一块一块扫过去,用树状数组kth就好。由于是寻找最大,可以通过变成最小。
#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;int c[MAXN*10], n, m, k, r = 2000001;inline int lowbit(int i) { return i&(-i); }void modify(int nd, int dt){ for (int i = nd; i <= r; i += lowbit(i)) c[i] += dt; }int sum(int nd){ int ans = 0; for (int i = nd; i; i -= lowbit(i)) ans += c[i]; return ans; }int query(int l, int r){ return sum(r)-sum(l-1); }int kth(int nd){int now = 0, dat = 0;for (int i = 1<<27; i; i >>= 1) {if (now+i <= r && dat+c[i+now] < nd)dat += c[i+now], now += i;}return now+1;}vector<int> add[MAXN*10];void solve(){long long ans = 0;for (int i = 1; i <= m; i++) {for (int j = 0; j < add[i].size(); j++) {if (add[i][j] > 0) modify(r-add[i][j]+1, 1);else modify(r-(-add[i][j])+1, -1);}long long rm = r-kth(k)+1;if (rm <= r) ans += rm*rm;}printf("%lld\n", ans);}int main(){scanf("%d%d%d", &n, &m, &k);m *= 2;for (int i = 1; i <= n; i++) {int r, a, b;scanf("%d%d%d", &r, &a, &b);if (a < 0) a += m;if (b < 0) b += m;if (a < b) add[a+1].push_back(r), add[b+1].push_back(-r);else if (a > b)add[1].push_back(r), add[b+1].push_back(-r), add[a+1].push_back(r);else add[1].push_back(r);}solve();return 0;}
居然可以把死人和活人变成一组......昏君啊...
#include <bits/stdc++.h>using namespace std;const int MAXN = 1000005;struct sheap {int lc, rc, dat;sheap() { lc = rc = 0; }} tree[MAXN];int merge(int a, int b){if (a == 0 || b == 0) return a+b;if (tree[a].dat > tree[b].dat) swap(a, b);swap(tree[a].lc, tree[a].rc);return tree[a].lc = merge(tree[a].lc, b), a;}set<int> st;int pop(int &a){int ret = tree[a].dat;st.insert(ret);a = merge(tree[a].lc, tree[a].rc);return ret;}int fa[MAXN];inline int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }int n, m;char str[10];int htp[MAXN], dat[MAXN];int main(){scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%d", &tree[i].dat), dat[i] = tree[i].dat, htp[i] = i;scanf("%d", &m);for (int i = 1; i <= m; i++) {scanf("%s", str);if (str[0] == 'M') {int x, y;scanf("%d%d", &x, &y);if (st.count(dat[x]) || st.count(dat[y])) continue;x = findf(x), y = findf(y);if (x != y) fa[x] = y, htp[y] = merge(htp[x], htp[y]);} else {int x;scanf("%d", &x);if (st.count(dat[x])) puts("0");elseprintf("%d\n", pop(htp[findf(x)]));}}return 0;}
堆+ST表。
堆维护表示从开始,终点在之间的区间最大值,每次取出最大值后将区间分裂。因此需要用ST表维护区间最大值和最大值位置。
#include <bits/stdc++.h>using namespace std;const int MAXN = 500005;long long f[MAXN][22];int pos[MAXN][22];long long a[MAXN];int n, k, l, r;inline long long max_val(int l, int r){int k = int(log2(r-l+1)+0.0000001);return max(f[l][k], f[r-(1<<k)+1][k]);}inline int max_pos(int l, int r){int k = int(log2(r-l+1)+0.0000001);if (f[l][k] >= f[r-(1<<k)+1][k]) return pos[l][k];return pos[r-(1<<k)+1][k];}struct query {int i, l, r;inline long long val(){return max_val(l, r)-a[i-1];}inline int pos(){return max_pos(l, r);}inline friend bool operator < (query a, query b){return a.val() < b.val();}};priority_queue<query> hp;int main(){scanf("%d%d%d%d", &n, &k, &l, &r);for (register int i = 1; i <= n; i++)scanf("%lld", &a[i]), a[i] += a[i-1];for (register int i = 1; i <= n; i++) f[i][0] = a[i], pos[i][0] = i;for (register int j = 1; j <= 21; j++)for (register int i = 1; i <= n; i++) {f[i][j] = f[i][j-1], pos[i][j] = pos[i][j-1];if (i+(1<<(j-1)) <= n && f[i+(1<<(j-1))][j-1] > f[i][j]) {f[i][j] = f[i+(1<<(j-1))][j-1];pos[i][j] = pos[i+(1<<(j-1))][j-1];}}for (register int i = 1; i <= n; i++)if (i+l-1 <= n)hp.push((query){i, i+l-1, min(n, i+r-1)});long long ans = 0;for (register int i = 1; i <= k; i++) {query tp = hp.top(); hp.pop();ans += tp.val();int p = tp.pos();if (p-1 >= tp.l) hp.push((query){tp.i, tp.l, p-1});if (p+1 <= tp.r) hp.push((query){tp.i, p+1, tp.r});}cout << ans << endl;return 0;}
非负!=正..
如果早做了这题大概就会做出省选相逢是问候了吧...
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;int fa[MAXN];inline int findf(int nd){ return fa[nd]!=nd?fa[nd] = findf(fa[nd]):nd; }inline int lowbit(int i) { return i&(-i); }long long c[MAXN], dat[MAXN];int n, m;void modify(int nd, int dt){ for (int i = nd; i <= n; i += lowbit(i)) c[i] += dt; }long long sum(int nd){ long long ans = 0; for (int i = nd; i; i -= lowbit(i)) ans += c[i]; return ans; }long long query(int l, int r){ return sum(r)-sum(l-1); }inline int read(){int a = 0, c;do c = getchar(); while (!isdigit(c));while (isdigit(c)) {a = a*10+c-'0';c = getchar();}return a;}int main(){scanf("%d", &n);for (register int i = 1; i <= n; i++) c[i] = read(), dat[i] = c[i], fa[i] = i;for (register int i = 1; i <= n; i++) if (i+lowbit(i)<=n) c[i+lowbit(i)] += c[i];fa[n+1] = n+1;scanf("%d", &m);for (register int i = 1; i <= m; i++) {int opt, l, r, d;opt = read(), l = read(), r = read();if (opt == 1) printf("%lld\n", query(l, r));else {while (l <= r) {d = dat[l];d = (int)(sqrt(d))-d;modify(l, d), dat[l] += d;if (dat[l] <= 1) fa[l] = l+1;l = findf(l+1);}}}return 0;}
比较关键的是完成治疗。
#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;int dat[MAXN*4], lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4];int change[MAXN*4], llen[MAXN*4], rlen[MAXN*4], len[MAXN*4], top = 0, root = 0;inline int new_node(int opl, int opr){ return ++top, l[top] = opl, r[top] = opr, top; }inline void pdw(int nd) // 1 nh / -1 hole{if (!change[nd]) return;change[lc[nd]] = change[rc[nd]] = change[nd]*(lc[nd]!=0);dat[nd] = (r[nd]-l[nd]+1)*(change[nd]==1);llen[nd] = rlen[nd] = len[nd] = (r[nd]-l[nd]+1)*(change[nd]==-1);change[nd] = 0;}inline void update(int nd){pdw(nd), pdw(lc[nd]), pdw(rc[nd]);if (l[nd] == r[nd]) {llen[nd] = rlen[nd] = len[nd] = (dat[nd]==0);return;}llen[nd] = llen[lc[nd]]+(dat[lc[nd]] == 0)*llen[rc[nd]];rlen[nd] = rlen[rc[nd]]+(dat[rc[nd]] == 0)*rlen[lc[nd]];len[nd] = max(max(len[lc[nd]], len[rc[nd]]), rlen[lc[nd]]+llen[rc[nd]]);dat[nd] = dat[lc[nd]]+dat[rc[nd]];}int pat[MAXN];void build(int &nd, int l, int r){nd = new_node(l, r);if (l == r) dat[nd] = pat[r];else build(lc[nd], l, (l+r)/2), build(rc[nd], (l+r)/2+1, r);update(nd);}void modify(int nd, int opl, int opr, int alc){if (opl > opr) return;pdw(nd);if (opl == l[nd] && opr == r[nd]) change[nd] = alc, pdw(nd);else {int mid = (l[nd]+r[nd])/2;if (opr <= mid) modify(lc[nd], opl, opr, alc);else if (opl > mid) modify(rc[nd], opl, opr, alc);else modify(lc[nd], opl, mid, alc), modify(rc[nd], mid+1, opr, alc);pdw(lc[nd]), pdw(rc[nd]), update(nd);}}int query(int nd, int opl, int opr){pdw(nd);if (opl == l[nd] && opr == r[nd]) return len[nd];else {int mid = (l[nd]+r[nd])/2;pdw(lc[nd]), pdw(rc[nd]);if (opr <= mid) return query(lc[nd], opl, opr);else if (opl > mid) return query(rc[nd], opl, opr);else return max(max(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr)),min(rlen[lc[nd]], mid-opl+1)+min(llen[rc[nd]], opr-mid));}}void dfs(int nd, int tab = 0){if (!nd) return;for (int i = 1; i <= tab; i++) putchar(' ');printf("[%d, %d], dat = %d, llen = %d, rlen = %d, len = %d, change = %d\n", l[nd], r[nd], dat[nd], llen[nd], rlen[nd], len[nd], change[nd]);dfs(lc[nd], tab+2), dfs(rc[nd], tab+2);}int query_sum(int nd, int opl, int opr){if (opl > opr) return 0;pdw(nd);int mid = (l[nd]+r[nd])/2;if (opl == l[nd] && opr == r[nd]) return dat[nd];else return query_sum(lc[nd], opl, min(opr, mid))+query_sum(rc[nd], max(opl, mid+1), opr);}int last_pos(int nd, int k) // in node nd, the first place to have k holes{if (l[nd] == r[nd]) return l[nd];pdw(nd), pdw(lc[nd]), pdw(rc[nd]);int lp = r[lc[nd]]-l[lc[nd]]+1-dat[lc[nd]];if (lp >= k) return last_pos(lc[nd], k);else return last_pos(rc[nd], k-lp);}void fix(int ll, int lr, int nl, int nr){int dt = query_sum(root, ll, lr);modify(root, ll, lr, -1);if (dt == 0) return;int k = last_pos(root, nl-1-query_sum(root, 1, nl-1)+dt);modify(root, nl, min(nr, k), 1);}inline int read(){int a = 0, c;do c = getchar(); while (!isdigit(c));while (isdigit(c))a = a*10+c-'0', c = getchar();return a;}int n, m;int main(){n = read(), m = read();for (int i = 1; i <= n; i++) pat[i] = 1;build(root, 1, n);for (int i = 1; i <= m; i++) {int tp, l, r, u, v;tp = read();if (tp == 0) l = read(), r = read(), modify(root, l, r, -1);else if (tp == 1) l = read(), r = read(), u = read(), v = read(), fix(l, r, u, v);else l = read(), r = read(), printf("%d\n", query(root, l, r));}return 0;}
线段树合并小技巧。
貌似空间是的?
#include <bits/stdc++.h>using namespace std;const int MAXN = 400005;struct node {int l, r, lc, rc, dat;} tree[MAXN*4];int top = 0, root = 0;long long c1, c2;int build(int l, int r, int pos){if (l == r) return tree[++top] = (node){l, r, 0, 0, 1}, top;int mid = (l+r)>>1, nd = ++top;tree[nd] = (node){l, r, 0, 0, 1};if (pos <= mid) return tree[nd].lc = build(l, mid, pos), nd;else return tree[nd].rc = build(mid+1, r, pos), nd;}int merge(int a, int b){if (a == 0 || b == 0) return a+b;// cerr << "Merging " << a << " " << b << endl;// display(a, 0), display(b, 0);c1 += (long long)tree[tree[a].lc].dat*tree[tree[b].rc].dat;c2 += (long long)tree[tree[b].lc].dat*tree[tree[a].rc].dat;tree[a].dat += tree[b].dat;tree[a].lc = merge(tree[a].lc, tree[b].lc);tree[a].rc = merge(tree[a].rc, tree[b].rc);return a;}int n;long long cnt = 0, cp = 0;int dfs(){int nd; scanf("%d", &nd);if (nd != 0) return build(1, n, nd);++cp;int t = cp;int x = dfs(), y = dfs();c1 = c2 = 0;int ret = merge(x, y);cnt += min(c1, c2);return ret;}int main(){scanf("%d", &n);dfs();printf("%lld\n", cnt);return 0;}