QAQ为什么高一小朋友们的题都这么难。。
代码仅供参考,禁止照抄哦 = v =
说明一下:read()函数是读入优化,你们直接写scanf就行了。namespace只是为了封装,可以不要。
然后我线段树的姿势可能和你们老师讲的不太一样,个人觉得这样写起来简洁一些,而且效率高。
Promble A
原题是hdu1166。简单的点修改和区间查询。因为太水了我就写的树状数组 = =
#include <cstdio>
#include <iostream>
#include <cstring>
#define lowbit(i) (i & -i)
using namespace std;
int read()
{
int sign = 1, n = 0; char c = getchar();
while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
return sign*n;
}
const int Nmax = 50005;
int N, a[Nmax];
namespace BIT {
int d[Nmax];
inline void init() { memset(d + 1, 0, sizeof(d[0]) * N); }
inline void Add(int pos, int x)
{
for (int i = pos; i <= N; i += lowbit(i))
d[i] += x;
}
inline int Query(int pos)
{
int res = 0;
for (int i = pos; i; i -= lowbit(i)) res += d[i];
return res;
}
}
int main()
{
for (int T = read(), cas = 1; T --; ++ cas) {
N = read();
for (int i = 1; i <= N; ++ i) a[i] = read();
using namespace BIT; init();
for (int i = 1; i <= N; ++ i) Add(i, a[i]);
char s[10]; printf("Case %d:\n", cas);
while (~scanf("%s", s) && s[0] != 'E') {
int i = read(), j = read();
if (s[0] == 'A') Add(i, j);
else if (s[0] == 'S') Add(i, -j);
else printf("%d\n", Query(j) - Query(i - 1));
}
}
return 0;
}
Problem B
原题是hdu1754。记录最大值即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define lc (u << 1)
#define rc (u << 1 | 1)
using namespace std;
int read()
{
int sign = 1, n = 0; char c = getchar();
while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
return sign*n;
}
const int Nmax = 200005;
int N, M;
int a[Nmax];
namespace SegmentTree {
int mmax[Nmax << 2];
inline void build(int u, int l, int r)
{
if (l == r) {
mmax[u] = a[l];
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid); build(rc, mid + 1, r);
mmax[u] = max(mmax[lc], mmax[rc]);
}
inline void update(int u, int l, int r, const int &pos, const int &x)
{
if (l == r) {
mmax[u] = x;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(lc, l, mid, pos, x);
else update(rc, mid + 1, r, pos, x);
mmax[u] = max(mmax[lc], mmax[rc]);
}
inline int Query(int u, int l, int r, int L, int R)
{
if (l == L && r == R) return mmax[u];
int mid = (l + r) >> 1;
if (R <= mid) return Query(lc, l, mid, L, R);
else if (L > mid) return Query(rc, mid + 1, r, L, R);
return max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));
}
}
int main()
{
while (~scanf("%d%d", &N, &M)) {
for (int i = 1; i <= N; ++ i) a[i] = read();
using namespace SegmentTree;
build(1, 1, N);
char c; int A, B;
while (M --) {
scanf(" %c", &c); A = read(); B = read();
if (c == 'Q') printf("%d\n", Query(1, 1, N, A, B));
else update(1, 1, N, A, B);
}
}
return 0;
}
Problem C
原题hdu1698。记录当前区间的颜色即可,如果不止一种颜色,就标记为0。修改的时候记得下放标记。
#include <cstdio>
#include <iostream>
#define lc (u << 1)
#define rc (u << 1 | 1)
using namespace std;
int read()
{
int sign = 1, n = 0; char c = getchar();
while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
return sign*n;
}
const int Nmax = 100005;
namespace SegmentTree {
int lazy[Nmax << 2];
void build(int u, int l, int r)
{
lazy[u] = 1; if (l == r) return;
int mid = (l + r) >> 1;
build(lc, l, mid); build(rc, mid + 1, r);
}
inline void Modify(int u, int l, int r, int L, int R, const int &x)
{
if (lazy[u] == x || (l == L && r == R)) { lazy[u] = x; return; }
if (lazy[u]) { lazy[lc] = lazy[rc] = lazy[u]; lazy[u] = 0; }
int mid = (l + r) >> 1;
if (R <= mid) Modify(lc, l, mid, L, R, x);
else if (L > mid) Modify(rc, mid + 1, r, L, R, x);
else {
Modify(lc, l, mid, L, mid, x);
Modify(rc, mid + 1, r, mid + 1, R, x);
}
}
inline int Query(int u, int l, int r)
{
if (lazy[u]) return (r - l + 1) * lazy[u];
int mid = (l + r) >> 1;
return Query(lc, l, mid) + Query(rc, mid + 1, r);
}
}
int N, Q;
int main()
{
for (int T = read(), cas = 1; T --; ++ cas) {
N = read(), Q = read();
using namespace SegmentTree;
build(1, 1, N);
for (int l, r, x; Q --; ) {
l = read(), r = read(), x = read();
Modify(1, 1, N, l, r, x);
}
printf("Case %d: The total value of the hook is %d.\n", cas, Query(1, 1, N));
}
return 0;
}
Problem D
原题hdu3308。记录左边连续上升的长度,右边连续上升的长度和整个区间连续上升的长度。
Pushup的时候合并一下就行了,然后Query横跨两个区间的时候也要考虑是否可以连续。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int read()
{
int sign = 1, n = 0; char c = getchar();
while(c < '0' || c > '9'){ if(c == '-') sign = -1; c = getchar(); }
while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
return sign*n;
}
const int Nmax = 100005;
int N, M;
int a[Nmax];
#define lc (u << 1)
#define rc (u << 1 | 1)
namespace SegmentTree {
int lsum[Nmax << 2], rsum[Nmax << 2], msum[Nmax << 2];
inline void Push_up(int u, int l, int r, int m)
{
if (a[m] < a[m + 1]) {
lsum[u] = (lsum[lc] == m - l + 1) ? (m - l + 1 + lsum[rc]) : lsum[lc];
rsum[u] = (rsum[rc] == r - m) ? (r - m + rsum[lc]) : rsum[rc];
msum[u] = max(max(msum[lc], msum[rc]), lsum[rc] + rsum[lc]);
} else {
lsum[u] = lsum[lc]; rsum[u] = rsum[rc];
msum[u] = max(msum[lc], msum[rc]);
}
}
void build(int u, int l, int r)
{
if (l == r) {
lsum[u] = rsum[u] = msum[u] = 1;
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid); build(rc, mid + 1, r);
Push_up(u, l, r, mid);
}
void Modify(int u, int l, int r, const int &pos, const int &x)
{
if (l == r) { a[pos] = x; return; }
int mid = (l + r) >> 1;
if (pos <= mid) Modify(lc, l, mid, pos, x);
else Modify(rc, mid + 1, r, pos, x);
Push_up(u, l, r, mid);
}
int Query(int u, int l, int r, int L, int R)
{
if (L == l && R == r) return msum[u];
int mid = (l + r) >> 1;
if (R <= mid) return Query(lc, l, mid, L, R);
if (L > mid) return Query(rc, mid + 1, r, L, R);
int res = max(Query(lc, l, mid, L, mid), Query(rc, mid + 1, r, mid + 1, R));
if (a[mid] < a[mid + 1]) res = max(res, min(mid - L + 1, rsum[lc]) + min(R - mid, lsum[rc]));
return res;
}
}
int main()
{
for (int T = read(); T --; ) {
N = read(), M = read();
for (int i = 1; i <= N; ++ i) a[i] = read();
using namespace SegmentTree;
build(1, 1, N); char s[10]; int a, b;
while (M --) {
scanf("%s", s); a = read(); b = read();
if (s[0] == 'Q') printf("%d\n", Query(1, 1, N, a + 1, b + 1));
else Modify(1, 1, N, a + 1, b);
}
}
return 0;
}
Problem E
原题hdu1542。扫描线入门。建议以后要准备省选&NOI的同学掌握。
具体做法请自行百度。建议自学,这种东西我估计老师也没法给你们讲懂的。
另外用到了离散化,这个也要先去学习一下。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int Lmax = 205;
int N;
struct Line {
double x, down, up;
int cover;
bool operator < (const Line &b) const { return x < b.x; }
}L[Lmax];
double h[Lmax];
#define lc (u << 1)
#define rc (u << 1 | 1)
namespace SegmentTree {
int cover[Lmax << 2]; double len[Lmax << 2];
inline void build(int u, int l, int r)
{
cover[u] = len[u] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(lc, l, mid); build(rc, mid + 1, r);
}
inline void Push_up(int u, int l, int r)
{
if (cover[u]) len[u] = h[r] - h[l - 1];
else len[u] = (l == r) ? 0 : len[lc] + len[rc];
}
void Update(int u, int l, int r, int L, int R, const int &x)
{
if (l == L && r == R) {
cover[u] += x;
Push_up(u, l, r); return;
}
int mid = (l + r) >> 1;
if (R <= mid) Update(lc, l, mid, L, R, x);
else if (L > mid) Update(rc, mid + 1, r, L, R, x);
else {
Update(lc, l, mid, L, mid, x);
Update(rc, mid + 1, r, mid + 1, R, x);
}
Push_up(u, l, r);
}
}
int main()
{
ios :: sync_with_stdio(false);
int cas = 0, M;
while (cin >> N && N) {
M = 0; double x1, x2, y1, y2;
for (int i = 1; i <= N; ++ i) {
cin >> x1 >> y1 >> x2 >> y2;
h[M] = y1;
L[M ++] = (Line) { x1, y1, y2, 1 };
h[M] = y2;
L[M ++] = (Line) { x2, y1, y2, -1 };
}
sort(h, h + M);
N = unique(h, h + M) - h;
sort(L, L + M); -- M;
printf("Test case #%d\n", ++cas);
using namespace SegmentTree;
double ans = 0. ; build(1, 1, N - 1);
for (int i = 0; i < M; ++ i) {
int l = lower_bound(h, h + N, L[i].down) - h + 1;
int r = lower_bound(h, h + N, L[i].up) - h;
Update(1, 1, N - 1, l, r, L[i].cover);
ans += len[1] * (L[i + 1].x - L[i].x);
}
printf("Total explored area: %0.2f\n\n", ans);
}
return 0;
}