noip2021线段树专项
Luogu P4198 楼房重建
考虑什么情况下能看到某个楼房,当且仅当它前面的楼房高度与原点 \((0,0)\) 的斜率都比它自己的小。
所以需要维护每个点的斜率和区间最大值,用来合并子树。
在 pushup 的时候注意一些细节就行了。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#define lc rt<<1
#define rc rt<<1|1
#define db double
using namespace std;
const int N = 1e5 + 5;
int n, m;
db c[N], mx[N << 2];
int len[N << 2];
void pushup1(int rt)
{
mx[rt] = max(mx[lc], mx[rc]);
}
int pushup2(db x, int l, int r, int rt)
{
if(mx[rt] <= x) return 0;
if(c[l] > x) return len[rt];
if(l == r) return c[l] > x;
int mid = (l + r) >> 1;
if(mx[lc] <= x) return pushup2(x, mid + 1, r, rc);
else return pushup2(x, l, mid, lc) + len[rt] - len[lc];
}
void upd(int x, int y, int l, int r, int rt)
{
if(l == r)
{
mx[rt] = (db)y / x;
len[rt] = 1;
return;
}
int mid = (l + r) >> 1;
if(x <= mid) upd(x, y, l, mid, lc);
else upd(x, y, mid + 1, r, rc);
pushup1(rt);
len[rt] = len[lc] + pushup2(mx[lc], mid + 1, r, rc);
}
int rd()
{
int x = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return x;
}
int main()
{
n = rd(), m = rd();
while(m--)
{
int x = rd(), y = rd();
c[x] = (db)y / x;
upd(x, y, 1, n, 1);
printf("%d\n", len[1]);
}
return 0;
}
// A.S.
HEOI2016/TJOI2016 排序
这题太妙了
首先二分一个答案 \(x\),将序列中大于 \(x\) 的数赋为 \(1\),小于 \(x\) 的数赋为 \(0\)
在对区间 \([l,r]\) 排序时,若为升序就将区间内的 \(0\) 都放到前面后面 \(1\) 都放到后面,反之则反之。因此只需要支持区间修改。
单点查询 \(q\) 上的值,若为 \(1\),说明 \(x\) 小了,否则 \(x\) 大了。
但是我们在修改时需要查询区间 \(1\) 的个数,所以还要维护个区间和,支持区间查询。
Code
#include <iostream>
#include <cstdio>
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
template <typename T>
void read(T &x)
{
x = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return;
}
template <typename T>
void write(T x)
{
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
const int N = 1e5 + 5;
int n, m, q, a[N], op[N], L[N], R[N];
int b[N], sum[N << 2], tag[N << 2];
void pushup(int rt)
{
sum[rt] = sum[ls] + sum[rs];
}
void pushdown(int l, int r, int rt)
{
if(tag[rt] != -1)
{
int mid = (l + r) >> 1;
sum[ls] = tag[rt] * (mid - l + 1);
sum[rs] = tag[rt] * (r - mid);
tag[ls] = tag[rt];
tag[rs] = tag[rt];
tag[rt] = -1;
}
}
void build(int l, int r, int rt, int x)
{
sum[rt] = 0, tag[rt] = -1;
if(l == r)
{
sum[rt] = a[l] >= x;
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls, x);
build(mid + 1, r, rs, x);
pushup(rt);
}
void upd(int L, int R, int v, int l, int r, int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
sum[rt] = v * (r - l + 1);
tag[rt] = v;
return;
}
pushdown(l, r, rt);
int mid = (l + r) >> 1;
upd(L, R, v, l, mid, ls);
upd(L, R, v, mid + 1, r, rs);
pushup(rt);
}
int qry(int L, int R, int l, int r, int rt)
{
if(l > R || r < L) return 0;
if(L <= l && r <= R) return sum[rt];
pushdown(l, r, rt);
int mid = (l + r) >> 1;
return qry(L, R, l, mid, ls) + qry(L, R, mid + 1, r, rs);
}
bool chk(int x)
{
build(1, n, 1, x);
for(int i = 1; i <= m; i++)
{
int cnt = qry(L[i], R[i], 1, n, 1);
upd(L[i], R[i], 0, 1, n, 1);
if(!op[i]) upd(R[i] - cnt + 1, R[i], 1, 1, n, 1);
else upd(L[i], L[i] + cnt - 1, 1, 1, n, 1);
}
return qry(q, q, 1, n, 1);
}
int main()
{
read(n), read(m);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= m; i++) read(op[i]), read(L[i]), read(R[i]);
read(q);
int l = 1, r = n, ans;
while(l <= r)
{
int mid = (l + r) >> 1;
if(chk(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
write(ans);
return 0;
}
// A.S.
JSOI2008 最大数
线段树板子,单点修改、区间查询
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
const ll maxn=200010;
ll n,m,d,t;
ll maxs[maxn<<2];
void pushup(ll rt)
{
maxs[rt]=max(maxs[rt<<1],maxs[rt<<1|1])%d;
}
void update(ll x,ll y,ll l,ll r,ll rt)
{
if(l==r)
{
maxs[rt]=y;
return;
}
ll mid=(l+r)>>1;
if(x<=mid) update(x,y,l,mid,rt<<1);
else update(x,y,mid+1,r,rt<<1|1);
pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt)
{
if(L<=l&&r<=R)
return maxs[rt];
ll mid=(l+r)>>1;
ll ret=-1e9;
if(L<=mid) ret=max(ret,query(L,R,l,mid,rt<<1));
if(mid<R) ret=max(ret,query(L,R,mid+1,r,rt<<1|1));
return ret;
}
int main()
{
scanf("%lld%lld",&m,&d);
for(int i=1;i<=m;i++)
{
char ch[5];
ll x;
scanf("%s%lld",ch,&x);
if(ch[0]=='A')
{
x=(x+t)%d,n++;
update(n,x,1,m,1);
}
else
{
t=query(n-x+1,n,1,m,1);
printf("%lld\n",t);
}
}
return 0;
}
// A.S.
AHOI2009 维护序列
线段树板子,区间乘、区间加、区间求和
Code
#include <bits/stdc++.h>
#define ll long long
#define ls (rt << 1)
#define rs (rt << 1 | 1)
using namespace std;
template <typename T>
void read(T &x)
{
x = 0; int f = 1; char c = getchar();
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f;
}
template <typename T>
void write(T x)
{
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
const int N = 1e5 + 5;
int n, m, p;
ll a[N];
ll sum[N << 2], add[N << 2], mul[N << 2];
void pushup(int rt)
{
sum[rt] = (sum[ls] + sum[rs]) % p;
}
void build(int l, int r, int rt)
{
mul[rt] = 1;
if(l == r)
{
sum[rt] = a[l] % p;
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls), build(mid + 1, r, rs);
pushup(rt);
}
void pushdown(int l, int r, int rt)
{
if(mul[rt] != 1)
{
sum[ls] = sum[ls] * mul[rt] % p;
sum[rs] = sum[rs] * mul[rt] % p;
mul[ls] = mul[ls] * mul[rt] % p;
mul[rs] = mul[rs] * mul[rt] % p;
add[ls] = add[ls] * mul[rt] % p;
add[rs] = add[rs] * mul[rt] % p;
mul[rt] = 1;
}
if(add[rt] != 0)
{
int mid = (l + r) >> 1;
sum[ls] = (sum[ls] + add[rt] * (mid - l + 1) % p) % p;
sum[rs] = (sum[rs] + add[rt] * (r - mid) % p) % p;
add[ls] = (add[ls] + add[rt]) % p;
add[rs] = (add[rs] + add[rt]) % p;
add[rt] = 0;
}
}
void upd_mul(int L, int R, ll k, int l, int r, int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
sum[rt] = sum[rt] * k % p;
mul[rt] = mul[rt] * k % p;
add[rt] = add[rt] * k % p;
return;
}
pushdown(l, r, rt);
int mid = (l + r) >> 1;
upd_mul(L, R, k, l, mid, ls);
upd_mul(L, R, k, mid + 1, r, rs);
pushup(rt);
}
void upd_add(int L, int R, ll k, int l, int r, int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
sum[rt] = (sum[rt] + k * (r - l + 1) % p) % p;
add[rt] = (add[rt] + k) % p;
return;
}
pushdown(l, r, rt);
int mid = (l + r) >> 1;
upd_add(L, R, k, l, mid, ls);
upd_add(L, R, k, mid + 1, r, rs);
pushup(rt);
}
ll qry(int L, int R, int l, int r, int rt)
{
if(l > R || r < L) return 0;
if(L <= l && r <= R) return sum[rt];
pushdown(l, r, rt);
int mid = (l + r) >> 1;
return (qry(L, R, l, mid, ls) + qry(L, R, mid + 1, r, rs)) % p;
}
int main()
{
read(n), read(p);
for(int i = 1; i <= n; i++) read(a[i]);
read(m);
build(1, n, 1);
while(m--)
{
int op, x, y;
ll k;
read(op), read(x), read(y);
if(op == 1) read(k), upd_mul(x, y, k, 1, n, 1);
else if(op == 2) read(k), upd_add(x, y, k, 1, n, 1);
else write(qry(x, y, 1, n, 1)), puts("");
}
return 0;
}
// A.S.
HNOI2012 永无乡
权值线段树(?
写的 fhq treap
用并查集维护一下子树,进行启发式合并,合并的时候就将小的那棵树的每个点依次插入到大的那棵树上。
查询第 \(k\) 大即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
template <typename T>
void read(T &x)
{
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return;
}
template <typename T>
void write(T x)
{
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
int n, m, q;
int cnt, fa[N], val[N], dat[N], siz[N], ls[N], rs[N];
int id[N];
int Newnode(int v)
{
cnt++;
val[cnt] = v;
dat[cnt] = rand();
siz[cnt] = 1;
ls[cnt] = rs[cnt] = 0;
id[v] = cnt;
return cnt;
}
int Find(int x)
{
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void pushup(int p)
{
siz[p] = siz[ls[p]] + siz[rs[p]] + 1;
}
void Split(int p, int v, int &x, int &y)
{
if(!p) {x = y = 0; return;}
if(val[p] <= v)
{
x = p;
Split(rs[p], v, rs[x], y);
}
else
{
y = p;
Split(ls[p], v, x, ls[y]);
}
pushup(p);
return;
}
int Merge(int x, int y)
{
if(!x || !y) return x + y;
if(dat[x] < dat[y])
{
rs[x] = Merge(rs[x], y);
pushup(x);
return x;
}
else
{
ls[y] = Merge(x, ls[y]);
pushup(y);
return y;
}
}
void Insert(int &rt, int p)
{
int x, y;
int v = val[p];
Split(rt, v, x, y);
rt = Merge(Merge(x, p), y);
return;
}
void dfs(int &x, int y)
{
if(!y) return;
dfs(x, ls[y]), dfs(x, rs[y]);
ls[y] = rs[y] = 0;
Insert(x, y);
return;
}
void Union(int x, int y)
{
x = Find(x), y = Find(y);
if(x == y) return;
if(siz[x] < siz[y]) swap(x, y);
int z = x;
dfs(z, y);
fa[x] = fa[y] = fa[z] = z;
return;
}
int Kth(int p, int k)
{
while(1)
{
if(siz[ls[p]] >= k) p = ls[p];
else if(siz[ls[p]] + 1 == k) return p;
else k -= siz[ls[p]] + 1, p = rs[p];
}
return p;
}
int main()
{
srand((unsigned)time(NULL));
read(n), read(m);
for(int i = 1, v; i <= n; i++)
{
read(v);
fa[i] = Newnode(v);
}
for(int i = 1, x, y, z; i <= m; i++)
{
read(x), read(y);
Union(x, y);
}
read(q);
while(q--)
{
char op[5];
int x, y;
scanf("%s", op);
read(x), read(y);
if(op[0] == 'Q')
{
if(siz[Find(x)] < y) puts("-1");
else write(id[val[Kth(Find(x), y)]]), puts("");
}
else Union(x, y);
}
return 0;
}
// A.S.
SCOI2010 序列操作
真·码农题
不过写的时候还是挺顺利的,就一个小错(但还是调了一晚上 qaq
区间最长连续个数就比较套路的记一下左边和右边的,再合并中间的。
但是因为有区间取反,所以 \(0\) 和 \(1\) 的连续最长的长度都要记下来,取反时 swap
一下。
在区间赋值时把区间取反标记去掉,但是在区间取反的时候不能去掉区间赋值标记。
所以 pushdown
里要先下传区间赋值,再下传区间取反。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
namespace IO
{
template <typename T>
void read(T &x)
{
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return;
}
template <typename T>
void write(T x)
{
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
}
using namespace IO;
namespace SegTree
{
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define swap(a, b) a ^= b ^= a ^= b
int len[N << 2], sum[N << 2], tag[N << 2], rev[N << 2];
int mx[N << 2][2], lmx[N << 2][2], rmx[N << 2][2];
void pushup(int rt)
{
sum[rt] = sum[ls] + sum[rs];
lmx[rt][0] = lmx[ls][0];
rmx[rt][0] = rmx[rs][0];
lmx[rt][1] = lmx[ls][1];
rmx[rt][1] = rmx[rs][1];
if(lmx[rt][0] == len[ls]) lmx[rt][0] += lmx[rs][0];
if(rmx[rt][0] == len[rs]) rmx[rt][0] += rmx[ls][0];
if(lmx[rt][1] == len[ls]) lmx[rt][1] += lmx[rs][1];
if(rmx[rt][1] == len[rs]) rmx[rt][1] += rmx[ls][1];
mx[rt][0] = max(max(mx[ls][0], mx[rs][0]), rmx[ls][0] + lmx[rs][0]);
mx[rt][1] = max(max(mx[ls][1], mx[rs][1]), rmx[ls][1] + lmx[rs][1]);
}
void Swap(int x)
{
swap(mx[x][0], mx[x][1]);
swap(lmx[x][0], lmx[x][1]);
swap(rmx[x][0], rmx[x][1]);
}
void pushdown(int rt)
{
if(~tag[rt])
{
sum[ls] = len[ls] * tag[rt];
sum[rs] = len[rs] * tag[rt];
tag[ls] = tag[rt];
tag[rs] = tag[rt];
rev[ls] = rev[rs] = 0;
int x = tag[rt], y = tag[rt] ^ 1;
mx[ls][x] = lmx[ls][x] = rmx[ls][x] = len[ls];
mx[rs][x] = lmx[rs][x] = rmx[rs][x] = len[rs];
mx[ls][y] = lmx[ls][y] = rmx[ls][y] = 0;
mx[rs][y] = lmx[rs][y] = rmx[rs][y] = 0;
tag[rt] = -1;
}
if(rev[rt])
{
sum[ls] = len[ls] - sum[ls];
sum[rs] = len[rs] - sum[rs];
rev[ls] ^= rev[rt];
rev[rs] ^= rev[rt];
Swap(ls), Swap(rs);
rev[rt] = 0;
}
}
void build(int l, int r, int rt)
{
len[rt] = r - l + 1;
tag[rt] = -1;
if(l == r)
{
int a; read(a);
sum[rt] = a;
mx[rt][a] = lmx[rt][a] = rmx[rt][a] = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
pushup(rt);
}
void update(int L, int R, int x, int l, int r, int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
if(~x)
{
sum[rt] = len[rt] * x;
tag[rt] = x;
rev[rt] = 0;
mx[rt][x] = lmx[rt][x] = rmx[rt][x] = len[rt];
mx[rt][x ^ 1] = lmx[rt][x ^ 1] = rmx[rt][x ^ 1] = 0;
}
else
{
rev[rt] ^= 1;
sum[rt] = len[rt] - sum[rt];
Swap(rt);
}
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
update(L, R, x, l, mid, ls);
update(L, R, x, mid + 1, r, rs);
pushup(rt);
}
int qry_cnt(int L, int R, int l, int r, int rt)
{
if(l > R || r < L) return 0;
if(L <= l && r <= R) return sum[rt];
pushdown(rt);
int mid = (l + r) >> 1;
return qry_cnt(L, R, l, mid, ls) + qry_cnt(L, R, mid + 1, r, rs);
}
int qry_max(int L, int R, int l, int r, int rt)
{
if(l > R || r < L) return 0;
if(L <= l && r <= R) return mx[rt][1];
pushdown(rt);
int mid = (l + r) >> 1, res = min(rmx[ls][1], mid - L + 1) + min(lmx[rs][1], R - mid);
return max(res, max(qry_max(L, R, l, mid, ls), qry_max(L, R, mid + 1, r, rs)));
}
}
using namespace SegTree;
namespace Acestar
{
void main()
{
int n, m;
read(n), read(m);
build(1, n, 1);
while(m--)
{
int op, l, r;
read(op), read(l), read(r);
l++ , r++;
if(op == 0) update(l, r, 0, 1, n, 1);
if(op == 1) update(l, r, 1, 1, n, 1);
if(op == 2) update(l, r, -1, 1, n, 1);
if(op == 3) write(qry_cnt(l, r, 1, n, 1)), putchar('\n');
if(op == 4) write(qry_max(l, r, 1, n, 1)), putchar('\n');
}
return;
}
}
int main()
{
Acestar :: main();
return 0;
}
// A.S.
数据结构题主要还是多写(
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$