@ljt12138
2017-05-28T18:17:56.000000Z
字数 13025
阅读 823
学习了一个斜堆的写法,非常好写,于是又去切了可并堆模板[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");
else
printf("%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;
}