这篇博客比较便于理解:Gakki
联系衔接:你想变强吗
单点更新区间加减懒惰标记这些都是基础、
POJ 2886
题意:给出n个人的名字和他手中的数字ai,n个人围成一圈,给出一个数字k,代表k这个人第一个出去,如果k这个人的数字是正数,代表顺时针走ak次的人出去,负数就逆时针,现在要求一个F(p)的最大值,F(p)代表p的因子个数,比如6 有因子个数 1 2 3 6所以F(6) = 4,令第i个人出去的值是F(i),现在要求F(i)的最大值,最大值相同输出最先出去的那个人
思路:首先我们可以用素数筛求出每个数的因子个数,那么我们就可以知道1到n中那个数的因子最多了,使用线段树维护这个区间还有多少人,假设第i轮是第p个出去,那么在区间里找到第p个位置即可,mod实际上代表的是还剩下多少个人,由于数很大,要进行模运算,但原本的位置是1 ~ mod, mod对应的是0 ~ mod - 1,所以这里我们要转化一下,把1~n转化到0~mod - 1,然后再转化回1 ~ n
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 5e5 + 10;
const int INF = 1e9 + 10;
bool isprime[qq];
int prime[qq];
int n, m, top;
char name[qq][15];
int num[qq];
int tree[qq << 2];
int factor[qq], of[qq];
void Init() {
top = 0;
for(int i = 0; i < qq; ++i) {
of[i] = i;
factor[i] = 1;
}
for(int i = 2; i < qq; ++i) {
if(!isprime[i]) {
prime[++top] = i;
}
for(int j = 1; j <= top && i * prime[j] < qq; ++j) {
isprime[i * prime[j]] = true;
if(i % prime[j] == 0) {
break;
}
}
}
for(int i = 1; i <= top; ++i) {
for(int j = prime[i]; j < qq; j += prime[i]) {
int cnt = 1;
while(of[j] % prime[i] == 0) {
of[j] /= prime[i];
cnt++;
}
factor[j] *= cnt;
}
}
}
void PushUp(int rt) {
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void BuildTree(int l, int r, int rt) {
tree[rt] = (r - l + 1);
if(l == r) return;
int m = (l + r) >> 1;
BuildTree(l, m, rt << 1);
BuildTree(m + 1, r, rt << 1 | 1);
}
int Change(int l, int r, int rt, int k) {
tree[rt]--;
if(l == r) return l;
int m = (l + r) >> 1;
if(tree[rt << 1] >= k) {
return Change(l, m, rt << 1, k);
}
return Change(m + 1, r, rt << 1 | 1, k - tree[rt << 1]);
}
map<int, bool> mp;
int main(){
Init();
int &mod = tree[1];
while(scanf("%d%d", &n, &m) != EOF) {
for(int i = 1; i <= n; ++i) {
scanf("%s %d", name[i], num + i);
}
BuildTree(1, n, 1);
int maxn = 0;
for(int i = 1; i <= n; ++i) {
maxn = max(maxn, factor[i]);
}
int cnt;
for(int i = 1; i <= n; ++i) {
if(maxn == factor[i]) {
cnt = i;
break;
}
}
int pos = 0;
num[pos] = 0;
for(int i = 0; i < cnt; ++i) {
if(num[pos] > 0) {
m = ((m + num[pos] - 2) % mod + mod) % mod + 1;
} else {
m = ((m + num[pos] - 1) % mod + mod) % mod + 1;
}
pos = Change(1, n, 1, m);
}
printf("%s %d\n", name[pos], maxn);
}
return 0;
}
POJ 2528
题意:有一块广告版长度无限不考虑宽度,顺序给出n个广告牌,它的会贴在广告牌的[ai, bi],顺序去贴广告牌,问最后能看到几个广告
思路:一个很简单的思路就是直接给区间[ai, bi]赋值标记数字,最后统计有多少不同的数字,但是范围太大了 达到了1e9,这样建树是不可的,但是注意n的范围很小,所以我们可以考虑把坐标给离散化,把出现的区间数字从小到大排序然后映射到区间较小的范围去,最后实现最开始的简单思路统计即可
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 1e4 + 10;
const int INF = 1e9 + 10;
int li[qq], ri[qq], num;
int x[qq << 3], col[qq << 4], ans;
bool hs[qq];
void Init() {
mst(col, -1);
mst(hs, false);
}
void PushDown(int rt) {
if(col[rt] != -1) {
col[rt << 1] = col[rt << 1 | 1] = col[rt];
col[rt] = -1;
}
}
int Search(int l, int r, int val) {
while(l <= r) {
int m = (l + r) >> 1;
if(x[m] == val) return m;
else if(x[m] < val) l = m + 1;
else r = m - 1;
}
return 0;
}
void UpDate(int l, int r, int rt, int x, int y, int val) {
if(x <= l && r <= y) {
col[rt] = val;
return;
}
PushDown(rt);
int m = (l + r) >> 1;
if(m >= x) UpDate(l, m, rt << 1, x , y, val);
if(m < y) UpDate(m + 1, r, rt << 1 | 1, x, y, val);
}
void Query(int l, int r, int rt) {
if(l == r) {
if(col[rt] == -1) return;
if(!hs[col[rt]]) ++ans;
hs[col[rt]] = true;
return;
}
PushDown(rt);
int m = (l + r) >> 1;
Query(l, m, rt << 1);
Query(m + 1, r, rt << 1 | 1);
}
int main(){
int t;
scanf("%d", &t);
while(t--) {
Init();
int n; scanf("%d", &n);
int m = 0;
for(int i = 0; i < n; ++i) {
scanf("%d%d", li + i, ri + i);
x[++m] = li[i];
x[++m] = ri[i];
}
sort(x + 1, x + 1 + m);
int num = 1;
for(int i = 2; i <= m; ++i) {
if(x[i] != x[i - 1]) x[++num] = x[i];
}
for(int i = num; i > 1; --i) {
if(x[i] - x[i - 1] > 1) x[++num] = x[i - 1] + 1;
}
sort(x + 1, x + 1 + num);
for(int i = 0; i < n; ++i) {
int l = Search(1, num, li[i]);
int r = Search(1, num, ri[i]);
UpDate(1, num, 1, l, r, i + 1);
}
ans = 0;
Query(1, num, 1);
printf("%d\n", ans);
}
return 0;
}
POJ2777
区间覆盖操作
题意:给出L,颜色总数T以及O次操作,每次操作
C 1 1 2 P 1 2第一种代表把区间1 1 内的点染成颜色2
第二种询问区间1 2内有多少种颜色
最初0 ~ L内的点都是颜色1
思路:线段树的区间覆盖问题,这题要好好体会一下,之后很多题都运用到了这种思想
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 100000 + 10;
const int INF = 1e9 + 10;
int color[qq << 2], lazy[qq << 2];
int n, T, Q;
void PushDown(int rt) {
if(color[rt] != -1) {
color[rt << 1] = color[rt << 1 | 1] = color[rt];
color[rt] = -1;
}
/*if(lazy[rt] != -1) {
lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = -1;
}*/
}
void Change(int l, int r, int rt, int x, int y, int val) {
if(x <= l && r <= y) {
color[rt] = val;
// lazy[rt] = val;
return;
}
PushDown(rt);
int m = (l + r) >> 1;
if(m >= x) Change(l, m, rt << 1, x, y, val);
if(m < y) Change(m + 1, r, rt << 1 | 1, x, y, val);
}
int ans;
bool hs[35];
void Query(int l, int r, int rt, int x, int y) {
if(color[rt] != -1) {
if(!hs[color[rt]]) ++ans;
hs[color[rt]] = true;
return;
}
PushDown(rt);
int m = (l + r) >> 1;
if(m >= x) Query(l, m, rt << 1, x, y);
if(m < y) Query(m + 1, r, rt << 1 | 1, x, y);
}
int main(){
scanf("%d%d%d", &n, &T, &Q);
char st[5];
int a, b, c;
color[1] = 1;
while(Q--) {
scanf("%s%d%d", st, &a, &b);
if(a > b) {
swap(a, b);
}
if(st[0] == 'P') {
mst(hs, false);
ans = 0;
Query(1, n, 1, a, b);
printf("%d\n", ans);
} else {
scanf("%d", &c);
Change(1, n, 1, a, b, c);
}
}
return 0;
}
POJ3225
这题是真的难想、看了聚聚们的分析才懂
参考:Gakki
说一点自己做这题的感想吧、首先这里要求的是区间,但我们线段树维护的只是点,所以这里用到了这种用偶数表示点奇数来表示区间的这样一种想法(太妙了), 之后就是区间之间的覆盖,覆盖很简单,但是难点在于区间01互换,这里用lazy标记来标志区间是否互换,覆盖的操作又可以去除区间01互换操作带来的影响。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define mst(a, b) memset(a, b, sizeof a)
#define REP(i, x, n) for(int i = x; i <= n; ++i)
const int MOD = 1e9 + 7;
const int qq = 65535 * 2 + 2;
const int INF = 1e9 + 10;
int cover[qq << 2], lazy[qq << 2];
bool mark[qq];
void PushDown(int rt) {
if(cover[rt] != -1) cover[rt] ^= 1;
else lazy[rt] ^= 1;
}
void PushDate(int rt) {
if(cover[rt] != -1) {
cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];
lazy[rt << 1] = lazy[rt << 1 | 1] = 0;
cover[rt] = -1;
}
if(lazy[rt]) {
PushDown(rt << 1);
PushDown(rt << 1 | 1);
lazy[rt] = 0;
}
}
void UpDate(int l, int r, int rt, int x, int y, int ch) {
if(x <= l && r <= y) {
if(ch == 'U') cover[rt] = 1, lazy[rt] = 0;
else if(ch == 'D') cover[rt] = lazy[rt] = 0;
else if(ch == 'S' || ch == 'C') PushDown(rt);
return;
}
PushDate(rt);
int m = (l + r) >> 1;
if(m >= x) UpDate(l, m, rt << 1, x, y, ch);
else if(ch == 'I' || ch == 'C') cover[rt << 1] = lazy[rt << 1] = 0;
if(m < y) UpDate(m + 1, r, rt << 1 | 1, x, y, ch);
else if(ch == 'I' || ch == 'C') cover[rt << 1 | 1] = lazy[rt << 1 | 1] = 0;
}
void Query(int l, int r, int rt) {
if(cover[rt] == 1) {
for(int i = l; i <= r; ++i) {
mark[i] = true;
}
return;
}
if(cover[rt] == 0) return;
if(l == r) return;
PushDate(rt);
int m = (l + r) >> 1;
Query(l, m, rt << 1);
Query(m + 1, r, rt << 1 | 1);
}
int main(){
int l, r;
char a, b, c;
while(scanf("%c %c%d,%d%c", &a, &b, &l, &r, &c) != EOF) {
l <<= 1, r <<= 1;
if(b == '(') l++;
if(c == ')') r--;
if(l > r) {
if(a == 'I' || a == 'C') cover[1] = lazy[1] = 0;
} else {
UpDate(0, qq, 1, l, r, a);
}
getchar();
}
bool f = false;
int s = -1, t = -1;
Query(0, qq, 1);
for(int i = 0; i < qq; ++i) {
if(mark[i]) s = (s == -1 ? i : s) , t = i;
else if(s != -1) {
if(f) printf(" ");
f = true;
printf("%c%d,%d%c", s & 1 ? '(' : '[', s >> 1, (t + 1) >> 1, t & 1 ? ')' : ']');
s = -1;
}
}
if(!f) printf("empty set");
puts("");
return 0;
}
UESTC 360
参考:聚聚