线段树学习、

这篇博客比较便于理解: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

参考:聚聚

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值