Preblom One
区间修改 +区间查询最值即可,需要离散化。
还有需要注意的地方就是一个方块仅仅是擦过另一个方块的左侧边或右侧边不算着陆。 为了处理这种信息,线段树维护的是每个格子,而不是数轴下标。
例如,[0, 1]
代表第一个格子,[1, 2]
代表第二个格子,以此类推 …
#define lc u << 1
#define rc u << 1 | 1
struct node{
int l, r, mx, tag;
}tr[2100 * 4];
void pushup(int u){
tr[u].mx = max(tr[lc].mx, tr[rc].mx);
}
void pushdown(int u){
if(tr[u].tag != -1){
tr[lc].mx = max(tr[lc].mx, tr[u].tag);
tr[rc].mx = max(tr[rc].mx, tr[u].tag);
tr[lc].tag = tr[rc].tag = tr[u].tag;
tr[u].tag = -1;
}
}
void build(int u, int l, int r){
tr[u] = {l, r, 0, -1};
if(l == r) return ;
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
int ask(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r){
return tr[u].mx;
}
int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u);
int res = -1e9;
if(l <= mid) res = max(res, ask(lc, l, r));
if(r > mid) res = max(res, ask(rc, l, r));
return res;
}
void modify(int u, int l, int r, int v){
if(l <= tr[u].l && r >= tr[u].r){
tr[u].mx = tr[u].tag = v;
return ;
}
int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u);
if(l <= mid) modify(lc, l, r, v);
if(r > mid) modify(rc, l, r, v);
pushup(u);
}
class Solution {
public:
vector<int> t, len;
vector<int> fallingSquares(vector<vector<int>>& positions) {
for(auto &bk : positions){
len.push_back(bk[1]); // 存储方块长度
bk[1] += bk[0]; // 占据方格右边界
bk[0] ++; // 左边为第 bk[0] 个方格
t.push_back(bk[0]);
t.push_back(bk[1]);
}
sort(t.begin(), t.end());
int sz = unique(t.begin(), t.end()) - t.begin();
cout << sz << '\n';
for(auto &bk : positions){
// cout << bk[0] << ' ' << bk[1] << '\n';
bk[0] = lower_bound(t.begin(), t.begin() + sz, bk[0]) - t.begin() + 1;
bk[1] = lower_bound(t.begin(), t.begin() + sz, bk[1]) - t.begin() + 1;
}
build(1, 1, sz);
for(auto bk : positions){
cout << bk[0] << ' ' << bk[1] << '\n';
}
int cnt = 0;
vector<int> res;
for(auto &bk : positions){
int t = ask(1, bk[0], bk[1]);
modify(1, bk[0], bk[1], t + len[cnt ++]);
res.push_back(ask(1, 1, sz));
// for(int i = 1; i <= sz; i ++){
// cout << ask(1, i, i) << " \n"[i == sz];
// }
}
return res;
}
};
Problem Two
还是线段树的基础应用,区间查询 + 区间修改。
还要用一下二分,我二分了第一次出现花的位置,和出现 x x x 朵花的最靠左的位置。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lc u << 1
#define rc u << 1 | 1
int const N = 5e4 + 10;
int const inf = 9e18;
struct node{
int l, r, sum;
int tag;
}tr[N << 2];
int n, m, k, a, b;
void pushup(int u){
tr[u].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int u){
if(tr[u].tag != inf){
tr[lc].sum = (tr[lc].r - tr[lc].l + 1) * tr[u].tag;
tr[rc].sum = (tr[rc].r - tr[rc].l + 1) * tr[u].tag;
tr[lc].tag = tr[rc].tag = tr[u].tag;
tr[u].tag = inf;
}
}
void build(int u, int l, int r){
tr[u] = {l, r, 0, inf};
if(l == r) return;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
void change(int u, int l, int r, int v){
if(l <= tr[u].l && r >= tr[u].r){
tr[u].sum = v * (tr[u].r - tr[u].l + 1);
tr[u].tag = v;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l <= mid) change(lc, l, r, v);
if(r > mid) change(rc, l, r, v);
pushup(u);
}
int askSum(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r){
return tr[u].sum;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
int res = 0;
if(l <= mid) res += askSum(lc, l, r);
if(r > mid) res += askSum(rc, l, r);
return res;
}
int askZeros(int l, int r){
return (r - l + 1) - askSum(1, l, r);
}
void solve(){
cin >> n >> m;
build(1, 1, n);
for(int i = 1; i <= m; i ++){
cin >> k >> a >> b;
if(k == 1){
++ a;
int t = askZeros(a, n);
if(t == 0){
cout << "Can not put any one.\n";
}
else{
b = b > t ? t : b;
int l = a, r = n;
while(l < r){
int mid = l + r >> 1;
if(askZeros(a, mid) != 0) r = mid;
else l = mid + 1;
}
cout << l - 1 << ' ';
int t = l;
l = a, r = n;
while(l < r){
int mid = l + r >> 1;
if(askZeros(a, mid) >= b) r = mid;
else l = mid + 1;
}
cout << l - 1 << '\n';
change(1, t, l, 1);
}
}
else{
++ a, ++ b;
int t = askSum(1, a, b);
change(1, a, b, 0);
cout << t << '\n';
}
// for(int i = 1; i <= n; i ++){
// cout << askSum(1, i, i) << " \n"[i == n];
// }
}
cout << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cout << "[pre]";
int T = 1;
cin >> T;
while (T --){
solve();
}
cout << "[/pre]";
return 0;
}
Problem Three
区间开平方 + 查询区间总和
x = 1 0 12 x=10^{12} x=1012 开根六次就会变成 1 1 1 。
所以我们维护每条线段的最值,进行区间开根时,如果一条线段 max = 1 \max = 1 max=1 ,那么我们遇到它时就不在对它进行开根。
由于每个叶子结点最多开根 6 6 6 次,树的高度为 log n \log n logn ,最多 n n n 个叶子结点,所以时间复杂度是 6 n log n 6n \log n 6nlogn
#define lc u << 1
#define rc u << 1 | 1
struct node{
int l, r, sum, mx;
}tr[N << 2];
void pushup(int u){
tr[u].sum = tr[lc].sum + tr[rc].sum;
tr[u].mx = max(tr[lc].mx, tr[rc].mx);
}
void build(int u, int l, int r){
tr[u] = {l, r, a[l], a[l]};
if(l == r) return ;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
// 区间开根
void segSqrt(int u, int l, int r){
if(tr[u].mx == 1) return ; // 优化
if(tr[u].l == tr[u].r){
int t = sqrt(tr[u].sum);
tr[u].sum = tr[u].mx = t;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) segSqrt(lc, l, r);
if(r > mid) segSqrt(rc, l, r);
pushup(u);
}
// 查询区间和
int askSum(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r){
return tr[u].sum;
}
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) res += askSum(lc, l, r);
if(r > mid) res += askSum(rc, l, r);
return res;
}
Problem Four
同第三题, x x x 取模之后要么不变,要么 ≤ x 2 \leq \frac x 2 ≤2x 。
所以对于一个数 v v v , log v \log v logv 次就会变为 1 1 1 。
关于下降一半多,以 x m o d y x \; mod \; y xmody 为例,如果 y ≤ x 2 y \leq \frac x 2 y≤2x ,那么一定满足。
如果 y > x 2 y > \frac x 2 y>2x , x m o d y = x − ⌊ x y ⌋ y = x − y < x 2 x \; mod \;y = x - \lfloor \frac x y \rfloor y = x - y <\frac x 2 xmody=x−⌊yx⌋y=x−y<2x 。
期望复杂度为 : O ( n log n log v ) O(n\log n\log v) O(nlognlogv)
#define lc u << 1
#define rc u << 1 | 1
struct node{
int l, r, sum, mx;
}tr[N << 2];
void pushup(int u){
tr[u].sum = tr[lc].sum + tr[rc].sum;
tr[u].mx = max(tr[lc].mx, tr[rc].mx);
}
void build(int u, int l, int r){
tr[u] = {l, r, a[l], a[l]};
if(l == r) return ;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
// 区间对 v 取模
void segMod(int u, int l, int r, int v){
if(v > tr[u].mx) return ;
if(tr[u].l == tr[u].r){
tr[u].sum %= v;
tr[u].mx = tr[u].sum;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) segMod(lc, l, r, v);
if(r > mid) segMod(rc, l, r, v);
pushup(u);
}
// 修改 a[p] = x;
void modify(int u, int p, int x){
if(tr[u].l == tr[u].r){
tr[u].sum = tr[u].mx = x;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(p <= mid) modify(lc, p, x);
else modify(rc, p, x);
pushup(u);
}
// 查询 summary[l ~ r]
int askSum(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r){
return tr[u].sum;
}
int res = 0;
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) res += askSum(lc, l, r);
if(r > mid) res += askSum(rc, l, r);
return res;
}
Problem Five
很有意思的一道题目,离散化 + 线段树即可。
特别注意下面这种样例,
10000 5
1 5
1 3
4 5
能看到三张海报,直接离散化却是两张,发现离散化的过程中丢失了信息。
所以将所有点加入到数组 t t t 排序准备离散化时,对于每个 t i < t i + 1 t_i<t_{i+1} ti<ti+1 ,在他们之间添加一个 ∈ [ t i + 1 , t i + 1 − 1 ] \in [t_i+1,t_{i+1}-1] ∈[ti+1,ti+1−1] 的点。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lc u << 1
#define rc u << 1 | 1
int const N = 3100;
int const inf = 2e18;
struct node{
int l, r, sum, tag;
}tr[N << 2];
int n, a[N], b[N];
int cnt = 0, t[N];
void pushup(int u){
tr[u].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int u){
if(tr[u].tag != inf){
tr[lc].sum = (tr[lc].r - tr[lc].l + 1) * tr[u].tag;
tr[rc].sum = (tr[rc].r - tr[rc].l + 1) * tr[u].tag;
tr[lc].tag = tr[rc].tag = tr[u].tag;
tr[u].tag = inf;
}
}
void build(int u, int l, int r){
tr[u] = {l, r, inf, inf};
if(l == r) return ;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
void change(int u, int l, int r, int v){
if(l <= tr[u].l && r >= tr[u].r){
tr[u].sum = v * (tr[u].r - tr[u].l + 1);
tr[u].tag = v;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l <= mid) change(lc, l, r, v);
if(r > mid) change(rc, l, r, v);
pushup(u);
}
int ask(int u, int l, int r){
if(l <= tr[u].l && r >= tr[u].r){
return tr[u].sum;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
int res = 0;
if(l <= mid) res += ask(lc, l, r);
if(r > mid) res += ask(rc, l, r);
return res;
}
// 离散化少东西了
void solve(){
cin >> n >> n;
for(int i = 1; i <= n; i ++){
cin >> a[i] >> b[i];
t[++ cnt] = a[i], t[++ cnt] = b[i];
}
sort(t + 1, t + cnt + 1);
vector<int> midPoint;
for(int i = 1; i + 1 <= cnt; i ++){
int l = t[i], r = t[i + 1];
if(r - l > 1){
midPoint.push_back(l + 1);
}
}
for(auto &x : midPoint) t[++ cnt] = x;
sort(t + 1, t + cnt + 1);
int sz = unique(t + 1, t + cnt + 1) - t - 1;
for(int i = 1; i <= n; i ++){
a[i] = lower_bound(t + 1, t + sz + 1, a[i]) - t;
b[i] = lower_bound(t + 1, t + sz + 1, b[i]) - t;
// cout << a[i] << ' ' << b[i] << '\n';
}
build(1, 1, sz);
for(int i = 1; i <= n; i ++){
change(1, a[i], b[i], i);
}
set<int> res;
for(int i = 1; i <= sz; i ++){
int t = ask(1, i, i);
if(t != inf) res.insert(t);
}
cout << res.size() << '\n';
}
https://www.luogu.com.cn/record/178161094
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
while (T --){
solve();
}
return 0;
}