洛谷每日一练5.8--P1972+P5482(BIT树状数组)

P1972

题意:

给 一 个 数 组 a i , q 个 询 问 , 每 次 询 问 一 个 区 间 有 多 少 个 不 同 的 数 给一个数组a_i,q个询问,每次询问一个区间有多少个不同的数 aiq

思路:

搞 一 个 数 组 b i 表 示 元 素 i 上 次 出 现 的 位 置 , 元 素 i 第 一 次 出 现 标 记 为 0 搞一个数组b_i表示元素i上次出现的位置,元素i第一次出现标记为0 biii0
例 如 : 对 于 a i : 1 例如:对于a_i:1 ai1 2 2 2 3 3 3 1 1 1 1 1 1 2 2 2 3 3 3 1 1 1 2 2 2 3 3 3 4 4 4
b i : 0 b_i: 0 bi:0 0 0 0 0 0 0 1 1 1 4 4 4 2 2 2 3 3 3 5 5 5 6 6 6 7 7 7 0 0 0
然 后 为 了 方 便 去 掉 了 0 , 就 把 全 部 加 一 即 : 然后为了方便去掉了0,就把全部加一即: 便0
b i : 1 b_i: 1 bi:1 1 1 1 1 1 1 2 2 2 5 5 5 3 3 3 4 4 4 6 6 6 7 7 7 8 8 8 1 1 1
然 后 对 于 每 个 询 问 就 是 询 问 i ∈ [ L , R ] , b [ i ] < L 的 个 数 然后对于每个询问就是询问i∈[L,R],b[i] < L的个数 i[L,R],b[i]<L
在 线 对 于 这 个 做 法 似 乎 有 点 不 方 便 , 所 有 用 了 离 线 。 在线对于这个做法似乎有点不方便,所有用了离线。 线便线
然 后 先 按 L 升 序 排 算 一 次 , 再 按 R 升 序 算 一 次 ; 然后先按L升序排算一次,再按R升序算一次; LR;
我 这 方 法 好 像 很 蠢 , 貌 似 有 更 好 的 方 法 的 我这方法好像很蠢,貌似有更好的方法的

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e6+10;
int n,q,a[N],b[N];
int pos[N];//表示i最新出现的位置 
int read(){
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}

int tree[N];
int lowbit(int x){
	return x&(-x);
}
void update(int x,int dx){
	while(x <= n){
		tree[x] += dx;
		x += lowbit(x);
	}
}
int query(int x){
	int res = 0;
	while(x){
		res += tree[x];
		x -= lowbit(x);
	}
	return res;
}
struct Node{
	int l,r,id;
}Q[N];
bool cmp1(Node a,Node b){
	return a.l < b.l;
}
bool cmp2(Node a,Node b){
	return a.r < b.r;
}
int ANS1[N],ANS2[N];
int main(){
	n = read();
	for(int i = 1;i <= n;i++){
		a[i] = read();
		b[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	for(int i = 1;i <= n;i++) b[i]++;
	int q;
	//scanf("%d",&q);
	q = read();
	for(int i = 1;i <= q;i++){
		Q[i].id = i;
		//scanf("%d%d",&Q[i].l,&Q[i].r);
		Q[i].l = read();Q[i].r = read();
	}
	sort(Q+1,Q+1+q,cmp1);
	int idx = 1;
	for(int i = 1;i <= q;i++){
		while(idx < Q[i].l){
			update(b[idx],1);
			idx++;
		}
		ANS1[Q[i].id] = query(Q[i].l);
	}
	
	memset(tree,0,sizeof(tree));
	sort(Q+1,Q+1+q,cmp2);
	idx = 1;
	for(int i = 1;i <= q;i++){
		while(idx <= Q[i].r){
			update(b[idx],1);
			idx++;
		}
		ANS2[Q[i].id] = query(Q[i].l);
	}
	
	for(int i = 1;i <= q;i++){
		printf("%d\n",ANS2[i]-ANS1[i]);
	}
	return 0;
}

P5482

题意:

支 持 三 个 操 作 : ① 加 入 一 条 不 等 式 ; ② 删 除 一 条 不 等 式 ; ③ 询 问 x = k 有 多 少 条 不 等 式 成 立 支持三个操作:①加入一条不等式;②删除一条不等式;③询问x=k有多少条不等式成立 x=k

思路:

可 以 发 现 ③ 操 作 的 k 范 围 挺 小 的 , 可 以 维 护 这 个 值 域 范 围 的 树 状 数 组 可以发现③操作的k范围挺小的,可以维护这个值域范围的树状数组 k
即 若 这 个 值 可 以 对 这 条 不 等 式 成 立 就 加 1 即若这个值可以对这条不等式成立就加1 1
对 于 添 加 不 等 式 的 操 作 : 对于添加不等式的操作:
① 首 先 一 个 比 较 容 易 的 a = 0 , 若 b > c 则 整 个 值 域 都 加 一 , 否 则 在 整 个 值 域 上 不 操 作 ; ①首先一个比较容易的a = 0,若b>c则整个值域都加一,否则在整个值域上不操作; a=0b>c,
② a > 0 , 简 单 移 项 可 得 x > c − b a , 先 对 不 等 式 左 边 的 值 判 断 下 如 果 超 过 了 1 e 6 ②a > 0,简单移项可得x > \frac{c-b}a,先对不等式左边的值判断下如果超过了1e6 a>0x>acb1e6
就 不 操 作 了 , 因 为 不 会 询 问 到 ; 否 则 v a l 和 − 1 e 6 取 个 m a x 到 1 e 6 都 是 解 就不操作了,因为不会询问到;否则val和-1e6取个max到1e6都是解 ;val1e6max1e6
$即这个范围加1(val = ⌊ c − b a ) ⌋ + 1 , 因 为 x > 4.5 的 整 数 解 都 是 大 于 等 于 5 ) \lfloor \frac{c-b}a) \rfloor+1,因为x > 4.5 的整数解都是大于等于5) acb)+1x>4.55
③ a < 0 类 似 , 移 项 可 得 x < c − b a , 先 判 断 下 左 边 是 否 已 经 小 于 − 1 e 6 ③a < 0类似,移项可得x < \frac{c-b}a,先判断下左边是否已经小于-1e6 a<0x<acb,1e6
小 于 了 就 不 操 作 了 , 否 则 − 1 e 6 到 v a l 和 1 e 6 取 的 m a x 都 是 解 小于了就不操作了,否则-1e6到val和1e6取的max都是解 1e6val1e6max
这 个 范 围 都 加 1 ( v a l = ⌈ c − b a ⌉ − 1 , 因 为 x < 5.3 的 整 数 解 都 小 于 等 于 5 ) 这个范围都加1(val = \lceil \frac{c-b}a \rceil-1,因为x < 5.3的整数解都小于等于5) 1(val=acb1,x<5.35)
为 了 方 便 去 掉 了 负 数 , 所 有 的 值 都 加 上 了 一 个 偏 移 量 为了方便去掉了负数,所有的值都加上了一个偏移量 便
还 有 一 个 就 是 可 能 会 多 次 删 除 一 个 不 等 式 还有一个就是可能会多次删除一个不等式
这 题 细 节 蛮 多 的 这题细节蛮多的

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int N = 1e5+10;
const int M = 2e6+10;
const int B = 1e6+1;//偏移量 
const int mx = 1e6+B;
//记下第i条不等式进行了什么操作 
int idx;
struct Node{
	int l,r;
	bool st;
}op[N];
//BIT Begin
int tree[M];
int lowbit(int x){
	return x&(-x);
}
int query(int x){
	int res = 0;
	while(x){
		res += tree[x];
		x -= lowbit(x);
	}
	return res;
}
void modify(int x,int dx){
	while(x <= mx){
		tree[x] += dx;
		x += lowbit(x);
	}
}
void update(int l,int r,int dx){
	modify(l,dx);modify(r+1,-dx);
}
//BIT End
void add(int a,int b,int c){
	if(a == 0){
		if(b > c){
			op[++idx].st = 1;
			op[idx].l = 1,op[idx].r = mx;
			update(op[idx].l,op[idx].r,1);
		}else{
			op[++idx].st = 0;
		}
	}else if(a > 0){
		int l = (int)floor(((c-b)*1.0)/a) + 1 + B;
		if(l > mx) op[++idx].st = 0;
		else{
			op[++idx].st = 1;
			op[idx].l = max(1,l),op[idx].r = mx;
			update(op[idx].l,op[idx].r,1);
		}
	}else{
		int r = int(ceil(((c-b)*1.0)/a)) - 1 + B;
		if(r < 1) op[++idx].st = 0;
		else{
			op[++idx].st = 1;
			op[idx].l = 1,op[idx].r = min(r,mx);
			update(op[idx].l,op[idx].r,1);
		}
	}
}
void del(int num){
	if(op[num].st){
		update(op[num].l,op[num].r,-op[num].st);
		op[num].st = 0;
	}
}
int main(){
	int n;scanf("%d",&n);
	char s[10];
	while(n--){
		scanf("%s",s);
		if(s[0] == 'A'){
			int a,b,c;scanf("%d%d%d",&a,&b,&c);
			add(a,b,c);
		}else if(s[0] == 'D'){
			int num;scanf("%d",&num);
			del(num);
		}else{
			int x;scanf("%d",&x);
			printf("%d\n",query(x+B));
		}
	}
	return 0;
} 
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值