首页 > 其他分享 >Luogu3168 [CQOI2015] 任务查询系统 - 主席树 - 二分 -

Luogu3168 [CQOI2015] 任务查询系统 - 主席树 - 二分 -

时间:2023-06-18 16:00:13浏览次数:48  
标签:二分 rt int 线段 Luogu3168 num 权值 CQOI2015 se

题目链接:https://www.luogu.com.cn/problem/P3168

题解:
主席树可以解决一类j静态区间第 \(k\) 小的问题,我们先来看看这是怎么工作的

  • 主席树的本质就是有很多棵线段树,然后发现这些线段树绝大多数都是重叠的,因此可以优化到空间复杂度 \(O(n\log n)\)
  • 在这里,我们将序列的每一个位置都开一棵权值线段树,一个位置的线段树中的每个节点就代表了这个权值出现的次数,求法就是上一个位置 + 当前位置的权值带来的影响。例如,\(a_3=5\),那么 \(rt[3]=rt[2], se[3][区间[5,5]] = se[2][区间[5,5]]+1\)。由于权值当做了下标,因此需要提前离散化。
  • 然后找某个区间 \([l,r]\) 的第 \(k\) 小的时候,就是利用第 \(r\) 棵线段树减去第 \(l-1\) 棵线段树求出这段区间里面各个权值的出现次数。然后在这个新的线段树上二分即可。

回到原问题,先将 \(p_i\) 离散化,再将区间问题利用差分化成单点修改,然后对于差分后的每个位置 \(t_i\) 建权值线段树。权值线段树维护两个东西:一个是 \(t_i\) 时刻仍然在运行的任务的权值在权值区间 \([l,r]\) 中出现了几次,一个是这些出现过的权值的和。

维护的时候需要注意,可以开两个 vector 分别记一下开始点和结束点,这样每建一棵权值线段树的时候就加上当前位置对应的开始区间的权值,删去当前位置结束的区间权值

有一些细节,比如只需要继承一次,然后也要考虑不增不删的情况

每次查询的时候,\(x\) 位置就在第 \(x\) 棵权值线段树中查询,前 \(k\) 小之和就在这棵权值线段树上二分。

代码:

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;

int n,qu;
int s[maxn], e[maxn], p[maxn], q[maxn], cnt=0;
int rt[maxn];
struct segm{
	int ls,rs,occ;
	ll sum;
}se[maxn << 5];
int clk = 0;
ll cf[maxn];
vector<int>st[maxn], ed[maxn];

void pushup(int num){
	int ls = se[num].ls, rs = se[num].rs;
	se[num].occ = se[ls].occ + se[rs].occ;
	se[num].sum = se[ls].sum + se[rs].sum;
}

void build(int l,int r,int &num){
	num = ++ clk;
	if(l == r){
		se[num].occ = 0;
		se[num].sum = 0;
		return ;
	}
	int mid = l+r>>1;
	build(l,mid,se[num].ls); build(mid+1,r,se[num].rs);
	pushup(num);
}

void update(int l,int r,int to,int sgn, int &num,int pre){
	num = ++ clk;
	se[num] = se[pre];
	if(l == r){
		se[num].occ += sgn;
		se[num].sum += sgn * q[to];
		return ;
	}
	int mid = l+r>>1;
	if(to <= mid)update(l,mid,to,sgn,se[num].ls,se[pre].ls);
	else update(mid+1,r,to,sgn,se[num].rs,se[pre].rs);
	pushup(num);
}

ll query(int l,int r,int rst,int num){
//	printf("hh %d %d %d\n",l,r,rst);
	if(l == r)return 1ll * se[num].sum / se[num].occ * rst;
	int mid = l+r>>1;
	int ls = se[num].ls;
	if(rst <= se[ls].occ)return query(l,mid,rst,ls);
	else return se[ls].sum + query(mid+1,r,rst - se[ls].occ,se[num].rs);
}

signed main(){
//	freopen("P3168_1.in","r",stdin);
	scanf("%d%d",&n,&qu);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&s[i],&e[i],&p[i]), q[++ cnt] = p[i];
	sort(q+1,q+cnt+1);
	cnt = unique(q+1,q+cnt+1) - (q+1);
	for(int i=1;i<=n;i++)
		p[i] = lower_bound(q+1,q+cnt+1,p[i]) - q;
	for(int i=1;i<=n;i++)
		st[s[i]].pb(p[i]), ed[e[i]+1].pb(p[i]);

	build(1,cnt,rt[0]);
	ll now = 0;
	for(int i=1;i<=n;i++){
		int g=0;
		for(int u : st[i])
			// 细节,只需要继承上一个位置的线段树一次 
			update(1,cnt,u,1,rt[i],g ? rt[i] : rt[i-1]), g=1;
//			printf("> %d %d\n",i,u);
		for(int u : ed[i])
			update(1,cnt,u,-1,rt[i],g ? rt[i] : rt[i-1]), g=1;
//			printf(">>> %d\n",u);
		// 如果没有增删,就只继承 
		if(g == 0)update(1,cnt,0,0,rt[i],rt[i-1]);
	}

	ll pre = 1;
	while(qu --){
		int x,a,b,c;scanf("%d%d%d%d",&x,&a,&b,&c);
		int k = 1 + (1ll*a*pre%c + b)%c;
//		int x,k;scanf("%d%d",&x,&k);
//		printf("?? %d\n",k);
		if(k > se[rt[x]].occ)printf("%lld\n",pre = se[rt[x]].sum);
		else printf("%lld\n",pre = query(1,cnt,k,rt[x]));
	}

	return 0;
}

标签:二分,rt,int,线段,Luogu3168,num,权值,CQOI2015,se
From: https://www.cnblogs.com/SkyRainWind/p/17489240.html

相关文章

  • Primes on Interval(欧拉筛+二分+滑动窗口)
    【题面】你决定用素数定理来做一个调查.众所周知,素数又被称为质数,其含义就是除了数字一和本身之外不能被其他任何的数字除尽.现在给定一个正整数序列 ,+1,⋯ ,a,a+1,⋯,b (≤)(a≤b),请找出一个最小值 l,使其满足对于任意一个长度为 l 的子串,都包含 k 个质数.......
  • Best Cow Fences(前缀和+特殊二分)
    之前的二分大多数都是整数类型的,今天又学到一种新型的二分,浮点数的二分,浮点数的二分可太巧妙了.且听我细细分说::OpenJudge-2018:BestCowFences#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intn,k;doublea[N],s[N],l,r;boolcheck(doubleu)......
  • 和与积 题解 简单二分查找
    题目大意:给定两个整数\(a(2\lea\le2\times10^9)\)和\(b(1\leb\le10^{18})\)。判断是否存在两个正整数\(x\)和\(y\),同时满足如下两个条件:\(x+y=a\)\(x\timesy=b\)解题思路:用\(a-x\)表示\(y\),可以得到面积的表达式为\(x\times(a-x)\),然后可以发现......
  • 整体二分学习笔记
    概念对于一个很多询问的题,假如对于一个询问可以二分处理,同时一次check可以只用\(n\)的时间处理所有询问的check结果,我们可以使用整体二分来做这个题。思想设函数\(\operatorname{solve}(S,L,R)\)为现在正在处理询问序列\(S\)里的询问,答案值域为\([L,R]\)。向下......
  • 二分答案
    概述二分答案即利用二分查找来得到答案,一般情况下,左边界$left$是$0$或者$1$;右边界$right$则视题目条件而定,取一个很大的数,然后利用二分查找的思想,来找到答案。二分答案的要求如果题目能够使用二分答案的思想来解决,那么$[left,right]$范围内,要满足二段性,即对$[left,......
  • Java基本查找,二分查找,选择排序
    一、基本查找packagecom.itheima.d8_sort_binarysearch;/***基本查找*/importjava.util.Scanner;publicclassTest3{publicstaticvoidmain(String[]args){//1、定义一个数组(基本查找)int[]arr={12,95,1,3,76,4,2,93,56,49,67};......
  • 二分答案
    概述二分答案即利用二分查找来得到答案,一般情况下,左边界$left$是$0$或者$1$;右边界$right$则视题目条件而定,取一个很大的数,然后利用二分查找的思想,来找到答案。二分答案的要求如果题目能够使用二分答案的思想来解决,那么$[left,right]$范围内,要满足二段性,即对$[left,......
  • Looksery Cup 2015-H. Degenerate Matrix(浮点数二分)
    原题链接H.DegenerateMatrixtimelimitpertestmemorylimitpertestinputoutputdeterminant ofamatrix 2 × 2degeneratenorm ||A|| ofamatrix AYouaregivenamatrix .Consideranydegeneratemat......
  • HDU 3277 Marriage Match III(并查集+二分+最大流)
    题意:和HDU3081一样的题意,只不过多了一个条件,每个女孩除了能选自己喜欢的男生之外,还能选不超过K个自己不喜欢的男生,问游戏最多能进行几轮思路:除了选喜欢的,还能选任意K个不喜欢的,怎么建图呢?一开始我想每个女孩连喜欢的男孩,而且选K个不喜欢的男孩也连边,可是这K个要怎么确定呢?这种显然......
  • HDU 3081 Marriage Match II(二分+并查集+最大流)
    题意:有N个女孩要与N个男孩玩配对游戏.每个女孩有一个可选男孩的集合(即该女孩可以选自己集合中的任意一个男孩作为该轮的搭档).然后从第一轮开始,每个女孩都要和一个不同的男孩配对.如果第一轮N个女孩都配对成功,那么就开始第二轮配对,女孩依然从自己的备选男孩集合中选择,但是不能......