总结:线段树最重要的就是pushup和pushdown两个函数。此外,根据题目需要,要清楚自己要维护的东西。
D - Mayor’s posters
这一题,首先x的范围很大,所以要先离散化,离散化之后,每两个点之间如果坐标值之差大于1的话,他们中间还得插一个值。否则的话,读者可以试试这组数据
3
5 6
4 5
6 8
如果没有插的话就会输出2了。
线段树维护区间是否被填满。
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 200050;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int all[maxn << 4];
void PushUp(int rt) { if (all[rt << 1] && all[rt << 1 | 1])all[rt] = 1; }
bool Update(int L, int R, int l, int r, int rt)
{
if (all[rt])return 0;
if (L <= l&&r <= R)
{
all[rt] = 1;
return 1;
}
//Pushdown(rt);
bool ans=0;
int m = l + r >> 1;
if (L <= m)ans =ans|Update(L, R, ls);
if (R > m)
ans = ans | Update(L, R, rs);
PushUp(rt);
return ans;
}
vector<int>V;
int Left[maxn], Right[maxn];
int Hash[10000005];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
memset(all, 0, sizeof(all));
V.clear();
int n;
scanf("%d", &n);
//int l, r;
for (int i = 1;i <= n;i++)
{
scanf("%d %d", &Left[i], &Right[i]);
V.push_back(Left[i]);
V.push_back(Right[i]);
}
sort(V.begin(), V.end());
int thesize=unique(V.begin(), V.end())-V.begin();
for (int i = 1;i < thesize;i++)
{
if (V[i] - V[i - 1] > 1)V.push_back(V[i] - 1);
}
sort(V.begin(), V.end());
thesize=unique(V.begin(), V.end())-V.begin();
for (int i = 0;i < thesize;i++)
Hash[V[i]] = i;
int ans = 0;
for (int i = n;i >= 1;i--)
{
int lidx = Hash[Left[i]]+1;
int ridx = Hash[Right[i]]+1;
if (Update(lidx, ridx, 1, thesize, 1))
ans++;
}
cout << ans << endl;
}
return 0;
}
H - Can you answer these queries?
这题有个隐藏的信息,一个不大于2^63的数,顶多开方7次,7次之后就会变成1了。
所以我们可以维护区间和,判断区间和和区间长度的关系来看是否要点更新。
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
const int maxn = 100005;
typedef long long ll;
ll sum[maxn << 2], A[maxn];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
void pushup(int rt) { sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; }
int n;
void build(int l, int r, int rt)
{
if (l == r) { sum[rt] = A[l];return; }
int m = l + r >> 1;
build(ls);
build(rs);
pushup(rt);
}
void update(int L, int l, int r, int rt)
{
if (l == r)
{
sum[rt] = sqrt(sum[rt]);
A[L] = sqrt(A[L]);
return;
}
int m = l + r >> 1;
if (L <= m)update(L, ls);
else update(L, rs);
pushup(rt);
}
void update1(int L, int R, int l, int r, int rt)
{
if (L <= l&&r <= R)
{
if (sum[rt] == r - l + 1)return;
for (int i = l;i <= r;i++)
{
if (A[i] == 1)continue;
update(i, 1, n, 1);
}
return;
}
int m = l + r >> 1;
if (L <= m)
update1(L, R, ls);
if (R > m)
update1(L, R, rs);
pushup(rt);
}
ll query(int L, int R, int l, int r, int rt)
{
if (L <= l&&r <= R)
return sum[rt];
int m = l + r >> 1;
ll ans = 0;
if (L <= m)ans += query(L, R, ls);
if (R > m)ans += query(L, R, rs);
return ans;
}
int main()
{
int cas = 1;
while (~scanf("%d",&n))
{
for (int i = 1;i <= n;i++)
scanf("%lld", &A[i]);
build(1, n, 1);
int q;
scanf("%d", &q);
int opt, x, y;
printf("Case #%d:\n", cas++);
while (q--)
{
scanf("%d %d %d", &opt, &x, &y);
if (x > y)swap(x, y);
if (opt == 0)
update1(x, y, 1, n, 1);
else
printf("%lld\n", query(x, y, 1, n, 1));
}
puts("");
}
return 0;
}
L - Vases and Flowers
根据题意,我们可以维护区间和然后二分找第一个放花的地方和最后一个放花的地方。
做完这题,终于会二分了。。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 50005;
int n, mn;
int sum[maxn << 2], lazy[maxn << 2];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
void pushup(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int ln, int rn)
{
if (lazy[rt] != -1)
{
sum[rt << 1] = ln*lazy[rt];
sum[rt << 1 | 1] = rn*lazy[rt];
lazy[rt << 1] = lazy[rt];
lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = -1;
}
}
void build(int l, int r, int rt)
{
if (l == r)
{
sum[rt] = 1;
lazy[rt] = -1;
return;
}
int m = l + r >> 1;
build(ls);
build(rs);
pushup(rt);
}
void update(int L, int R, int C, int l, int r, int rt)
{
if (L <= l&&r <= R)
{
sum[rt] = (r - l + 1)*C;
lazy[rt] = C;
return;
}
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (L <= m)
update(L, R, C, ls);
if (R > m)
update(L, R, C, rs);
pushup(rt);
}
int query(int L, int R, int l, int r, int rt)
{
if (L <= l&&r <= R)
return sum[rt];
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
int ans = 0;
if (L <= m)
ans += query(L, R, ls);
if (R > m)
ans += query(L, R, rs);
return ans;
}
int queryleft(int x, int y)//yes
{
int l = x, r = n - 1, mid;
int tmp;
int ans = -1;
while (l <= r)
{
mid = l + r >> 1;
tmp = query(mid, n - 1, 0, n - 1, 1);
if (tmp < y)
r = mid - 1;
else
l = mid + 1, ans = mid;
}
return ans;
}
int queryright(int left, int x, int y)
{
int l = left, r = n - 1, mid;
int tmp;
int ans = -1;
while (l <= r)
{
mid = l + r >> 1;
tmp = query(left, mid, 0, n - 1, 1);
if (tmp < y)
l = mid + 1;
else
r = mid - 1;
if (tmp == y)
ans = mid;
}
return ans;
}
void solve(int x, int y)
{
int tmp = query(x, n, 0, n - 1, 1);
if (tmp == 0 || y == 0)
{
puts("Can not put any one.");
return;
}
y = min(y, tmp);
int left = queryleft(x, tmp);
int right = queryright(left, x, y);
update(left, right, 0, 0, n - 1, 1);
printf("%d %d\n", left, right);
}
int main()
{
int T;
scanf("%d", &T);
int flag = 1;
while (T--)
{
memset(lazy, -1, sizeof(lazy));
scanf("%d %d", &n, &mn);
build(0, n - 1, 1);
int opt, x, y;
while (mn--)
{
scanf("%d %d %d", &opt, &x, &y);
if (opt == 1)
solve(x, y);
else
{
int ans = query(x, y, 0, n - 1, 1);
ans = (y - x + 1) - ans;
update(x, y, 1, 0, n - 1, 1);
printf("%d\n", ans);
}
/*for (int i = 0;i < n;i++)
printf("%d:%d\n", i, query(i, i, 0, n-1, 1));*/
}
puts("");
}
return 0;
}
M - 约会安排
我们可以开两个线段树,一个是维护屌丝未使用的最长连续时间,一个是维护女神未使用的最长连续时间。那么更新时候怎么更新呢。
如果是屌丝找的话,那么只看屌丝的线段树。如果是女神找的话,我们优先看未使用的时间段,所以先看屌丝的线段树,如果有的话,那么更新。如果没有的话,再看女神的线段树。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100005;
struct node
{
int lds, rds, lns, rns, maxds, maxns;
int lazyds, lazyns;
node(int _ld, int _rd, int _ln, int _rn, int _ds, int _ns, int _lds, int _lns) :lds(_ld), rds(_rd), lns(_ln), rns(_rn), maxds(_ds), maxns(_ns), lazyds(_lds), lazyns(_lns) {}
node() {}
}nodes[maxn << 2];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
void pushup(int rt, int l, int r)
{
nodes[rt].lds = nodes[rt << 1].lds;
nodes[rt].rds = nodes[rt << 1 | 1].rds;
int m = l + r >> 1;
if (nodes[rt << 1].lds == m - l + 1)
nodes[rt].lds += nodes[rt << 1 | 1].lds;
if (nodes[rt << 1 | 1].rds == r - m)
nodes[rt].rds += nodes[rt << 1].rds;
nodes[rt].maxds = max(nodes[rt << 1].maxds, nodes[rt << 1 | 1].maxds);
nodes[rt].maxds = max(nodes[rt].maxds, nodes[rt << 1].rds + nodes[rt << 1 | 1].lds);
nodes[rt].lns = nodes[rt << 1].lns;
nodes[rt].rns = nodes[rt << 1 | 1].rns;
if (nodes[rt << 1].maxns == m - l + 1)
nodes[rt].lns += nodes[rt << 1 | 1].lns;
if (nodes[rt << 1 | 1].maxns == r - m)
nodes[rt].rns += nodes[rt << 1].rns;
nodes[rt].maxns = max(nodes[rt << 1].maxns, nodes[rt << 1 | 1].maxns);
nodes[rt].maxns = max(nodes[rt].maxns, nodes[rt << 1].rns + nodes[rt << 1 | 1].lns);
}
void build(int l, int r, int rt)
{
nodes[rt] = node(r - l + 1, r - l + 1, r - l + 1, r - l + 1, r - l + 1, r - l + 1, 0, 0);
if (l == r)return;
int m = l + r >> 1;
build(ls);
build(rs);
}
void pushdown(int rt, int ln, int rn)
{
if (nodes[rt].lazyds)
{
if (nodes[rt].lazyds == 1)
{
nodes[rt << 1].lds = 0;
nodes[rt << 1].rds = 0;
nodes[rt << 1].maxds = 0;
nodes[rt << 1].lazyds = 1;
nodes[rt << 1 | 1].lds = 0;
nodes[rt << 1 | 1].rds = 0;
nodes[rt << 1 | 1].maxds = 0;
nodes[rt << 1 | 1].lazyds = 1;
}
else
{
nodes[rt << 1].lds = ln;
nodes[rt << 1].rds = ln;
nodes[rt << 1].maxds = ln;
nodes[rt << 1].lazyds = 2;
nodes[rt << 1 | 1].lds = rn;
nodes[rt << 1 | 1].rds = rn;
nodes[rt << 1 | 1].maxds = rn;
nodes[rt << 1 | 1].lazyds = 2;
}
nodes[rt].lazyds = 0;
}
if (nodes[rt].lazyns)
{
if (nodes[rt].lazyns == 1)
{
nodes[rt << 1].lns = 0;
nodes[rt << 1].rns = 0;
nodes[rt << 1].maxns = 0;
nodes[rt << 1].lazyns = 1;
nodes[rt << 1 | 1].lns = 0;
nodes[rt << 1 | 1].rns = 0;
nodes[rt << 1 | 1].maxns = 0;
nodes[rt << 1 | 1].lazyns = 1;
}
else
{
nodes[rt << 1].lns = ln;
nodes[rt << 1].rns = ln;
nodes[rt << 1].maxns = ln;
nodes[rt << 1].lazyns = 2;
nodes[rt << 1 | 1].lns = rn;
nodes[rt << 1 | 1].rns = rn;
nodes[rt << 1 | 1].maxns = rn;
nodes[rt << 1 | 1].lazyns = 2;
}
nodes[rt].lazyns = 0;
}
}
void update(int type, int L, int R, int C, int l, int r, int rt)
{
if (L <= l&&r <= R)
{
if (type == 1)
{
if (C == 1)
{
nodes[rt].lds = 0;
nodes[rt].rds = 0;
nodes[rt].maxds = 0;
nodes[rt].lazyds = 1;
}
else
{
nodes[rt].lds = r - l + 1;
nodes[rt].rds = r - l + 1;
nodes[rt].maxds = r - l + 1;
nodes[rt].lazyds = 2;
}
}
else
{
if (C == 1)
{
nodes[rt].lns = 0;
nodes[rt].rns = 0;
nodes[rt].maxns = 0;
nodes[rt].lazyns = 1;
}
else
{
nodes[rt].lns = r - l + 1;
nodes[rt].rns = r - l + 1;
nodes[rt].maxns = r - l + 1;
nodes[rt].lazyns = 2;
}
}
return;
}
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (L <= m)
update(type, L, R, C, ls);
if (R > m)
update(type, L, R, C, rs);
pushup(rt, l, r);
}
int query(int C, int type, int l, int r, int rt)
{
if (l == r)return l;
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (type == 1)
{
if (nodes[rt << 1].maxds >= C)
return query(C, type, ls);
else if (nodes[rt << 1].rds + nodes[rt << 1 | 1].lds >= C)
return m - nodes[rt << 1].rds + 1;
else
return query(C, type, rs);
}
else
{
if (nodes[rt << 1].maxns >= C)
return query(C, type, ls);
else if (nodes[rt << 1].rns + nodes[rt << 1 | 1].lns >= C)
return m - nodes[rt << 1].rns + 1;
else
return query(C, type, rs);
}
}
int query2(int L, int l, int r, int rt)
{
if (l == r)return nodes[rt].lds;
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (L <= m)return query2(L, ls);
else
return query2(L, rs);
}
int query3(int L, int l, int r, int rt)
{
if (l == r)return nodes[rt].lns;
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (L <= m)return query3(L, ls);
else
return query3(L, rs);
}
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
printf("Case %d:\n", cas++);
int n, m;
scanf("%d %d", &n, &m);
build(1, n, 1);
char s[10];
int x, y;
for (int i = 1;i <= m;i++)
{
scanf("%s %d", s, &x);
if (s[0] == 'D')
{
int time = nodes[1].maxds;
if (time < x)
puts("fly with yourself");
else
{
int q = query(x, 1, 1, n, 1);
update(1, q, q + x - 1, 1, 1, n, 1);
printf("%d,let's fly\n", q);
}
}
else if (s[0] == 'N')
{
int time = nodes[1].maxds;
if (time < x)
{
if (x > nodes[1].maxns)
puts("wait for me");
else
{
int q = query(x, 2, 1, n, 1);
update(2, q, q + x - 1, 1, 1, n, 1);
update(1, q, q + x - 1, 1, 1, n, 1);
printf("%d,don't put my gezi\n", q);
}
}
else
{
int q = query(x, 1, 1, n, 1);
update(1, q, q + x - 1, 1, 1, n, 1);
update(2, q, q + x - 1, 1, 1, n, 1);
printf("%d,don't put my gezi\n", q);
}
}
else
{
scanf("%d", &y);
update(1, x, y, 0, 1, n, 1);
update(2, x, y, 0, 1, n, 1);
puts("I am the hope of chinese chengxuyuan!!");
}
/*for (int j = 1;j <= n;j++)
printf("%d:%d %d\n", j, query2(j, 1, n, 1),query3(j,1,n,1));*/
}
}
return 0;
}
这题主要是线段树维护最长连续区间的模板。
Q - Get The Treasury
题意:给了三维坐标系中n个长方体,求重叠大于2次的体积。
我们可以将体积分成若干个高为1的长方体来算,那么就转换成求面积了。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<map>
using namespace std;
const int maxn = 1005;
int X[maxn << 1];
struct line
{
int l, r, h;
int flag;
line(int _l,int _r,int _h,int _flag):l(_l),r(_r),h(_h),flag(_flag){}
line(){}
bool operator<(const line &b)const
{
return h < b.h;
}
}lines[1005][maxn << 1];
struct node
{
int l, r;
int cnt;
int len1, len2, len3;
node(int _l, int _r, int _c, int _l1, int _l2, int _l3) :l(_l), r(_r), cnt(_c), len1(_l1), len2(_l2), len3(_l3){}
node(){}
}nodes[maxn << 3];
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
void build(int l, int r, int rt)
{
nodes[rt] = node(l, r, 0, 0, 0, 0);
if (l == r)return;
int m = l + r >> 1;
build(ls);
build(rs);
}
void pushup(int rt)
{
int lidx = nodes[rt].l;
int ridx = nodes[rt].r + 1;
if (nodes[rt].cnt)
nodes[rt].len1 = X[ridx] - X[lidx];
else if (nodes[rt].l == nodes[rt].r)
nodes[rt].len1 = 0;
else
nodes[rt].len1 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;
if (nodes[rt].cnt > 1)
nodes[rt].len2 = X[ridx] - X[lidx];
else if (nodes[rt].l == nodes[rt].r)
nodes[rt].len2 = 0;
else
if (nodes[rt].cnt == 1)
{
nodes[rt].len2 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;
}
else
nodes[rt].len2 = nodes[rt << 1].len2 + nodes[rt << 1 | 1].len2;
if (nodes[rt].cnt > 2)
nodes[rt].len3 = X[ridx] - X[lidx];
else if (nodes[rt].l == nodes[rt].r)
nodes[rt].len3 = 0;
else
{
if (nodes[rt].cnt == 2)
{
nodes[rt].len3 = nodes[rt << 1].len1 + nodes[rt << 1 | 1].len1;
}
else if (nodes[rt].cnt == 1)
{
nodes[rt].len3 = nodes[rt << 1].len2 + nodes[rt << 1 | 1].len2;
}
else
nodes[rt].len3 = nodes[rt << 1].len3 + nodes[rt << 1 | 1].len3;
}
}
void update(int L, int R, int C, int l, int r, int rt)
{
if (L <= l&&r <= R)
{
nodes[rt].cnt += C;
pushup(rt);
return;
}
int m = l + r >> 1;
if (L <= m)
update(L, R, C, ls);
if (R > m)
update(L, R, C, rs);
pushup(rt);
}
typedef long long ll;
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
int n;
scanf("%d", &n);
int x1, y1, z1, x2, y2, z2;
int lnum[1005];
memset(lnum, 0, sizeof(lnum));
int xnum=0;
for (int i = 1;i <= n;i++)
{
scanf("%d %d %d %d %d %d", &x1, &y1, &z1, &x2, &y2, &z2);
for (int j = z1+500;j < z2+500;j++)
{
lines[j][lnum[j]++] = line(x1, x2, y1, 1);
lines[j][lnum[j]++] = line(x1, x2, y2, -1);
}
X[xnum++] = x1;
X[xnum++] = x2;
}
for (int i = 0;i <= 1000;i++)
sort(lines[i], lines[i] + lnum[i]);
sort(X, X + xnum);
xnum = unique(X, X + xnum) - X;
ll ans = 0;
for (int i = 0;i <= 1000;i++)
{
build(0, xnum - 1, 1);
for (int j = 0;j < lnum[i];j++)
{
int lidx = lower_bound(X, X + xnum, lines[i][j].l) - X;
int ridx = lower_bound(X, X + xnum, lines[i][j].r) - X;
ridx--;
update(lidx, ridx, lines[i][j].flag, 0, xnum - 1, 1);
ans += 1LL * nodes[1].len3*(lines[i][j + 1].h - lines[i][j].h);
}
//cout << ans << endl;
}
printf("Case %d: %lld\n", cas++, ans);
}
return 0;
}
仔细体会一下这个pushup。