线段树/扫描线问卷调查反馈——Rmq Problem / mex(主席树),Boring Queries(二分+st表+主席树),Colorful Squares(扫描线)
文章目录
- Rmq Problem / mex
- Boring Queries
- Colorful Squares
Rmq Problem / mex
luogu4137
对aia_iai建权值线段树
再加上可持久化
对于第RRR个版本的线段树,每个叶子xxx存的是ai=xa_i=xai=x的所有iii中最大的小于RRR的位置iii
那么询问[L,R][L,R][L,R]就转化成RRR版本线段树中所有小于LLL的叶子中的最小值
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 200005
int n, Q, cnt;
int a[maxn], rt[maxn];
struct node { int l, r, Min; }t[maxn * 50];void modify( int lst, int &now, int l, int r, int val, int pos ) {if( ! now ) now = ++ cnt;if( l == r ) { t[now].Min = pos; return; }int mid = ( l + r ) >> 1;if( val <= mid ) {t[now].r = t[lst].r;modify( t[lst].l, t[now].l, l, mid, val, pos );}else {t[now].l = t[lst].l;modify( t[lst].r, t[now].r, mid + 1, r, val, pos );}t[now].Min = min( t[t[now].l].Min, t[t[now].r].Min );
}int query( int now, int l, int r, int ti ) {if( l == r ) return l;int mid = ( l + r ) >> 1;if( t[t[now].l].Min <= ti ) return query( t[now].l, l, mid, ti );else return query( t[now].r, mid + 1, r, ti );
}int main() {scanf( "%d %d", &n, &Q );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );for( int i = 1;i <= n;i ++ ) {a[i] = min( a[i], n );modify( rt[i - 1], rt[i], 0, n, a[i], i );}for( int i = 1, l, r;i <= Q;i ++ ) {scanf( "%d %d", &l, &r );printf( "%d\n", query( rt[r], 0, n, l - 1 ) );}return 0;
}
Boring Queries
CF1422F
ai∈[1,2e5]→a_i\in[1,2e5]\rightarrowai∈[1,2e5]→ aia_iai分解质因子后,最多只会有一个>2e5≈450>\sqrt{2e5}≈450>2e5≈450的质因数
而在450
内的质因数有87
个
所以按质因数大小分块
预处理aia_iai含有的小于450
的质因数primej\rm prime_jprimej的个数
lcm\rm lcmlcm可以表示为∏maxlrpi\prod\max_l^rp_i∏maxlrpi(每一个质因数的个数最大值的乘积)
对于每个质因数开一个ststst表,开878787个就行,预处理出该质因数在a[l,r]a[l,r]a[l,r]内出现次数的最大值
剩下的一个>450>450>450的质因数,利用线段树来维护区间[l,r][l,r][l,r]内所有这些大质因数乘积
- 如果没有就往里面扔111
- 如果有
- 考虑对于区间[l,r][l,r][l,r]内多次出现的xxx,只能统计一次
- 记prei:aipre_i:a_iprei:ai上一次出现的位置
- 只在区间[l,r][l,r][l,r]的xxx第一次出现的时候把xxx统计进答案
- 这些位置的特点是prei≤l−1pre_i\le l-1prei≤l−1
- 所以相当于∀l≤i≤r∏i[prei≤l−1]ai\forall_{l\le i\le r} \prod_i\Big[pre_i\le l-1\Big]a_i∀l≤i≤r∏i[prei≤l−1]ai
- 主席树维护,rt[r]rt[r]rt[r]的叶子维护该值距离rrr最近的位置
- 当时候直接查rt[l−1]rt[l-1]rt[l−1]就行
跟第一题很类似的主席树维护
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define mod 1000000007
#define maxn 200005
struct node { int l, r, val; }t[maxn * 20];
int n, Q, cnt, tot;
bool vis[455];
int prime[90], a[maxn], rt[maxn], nxt[maxn], pos[maxn];
int mi[90][20];
char st[90][maxn][20];void sieve() {for( int i = 2;i <= 450;i ++ ) {if( ! vis[i] ) prime[++ cnt] = i;for( int j = 1;j <= cnt and i * prime[j] <= 450;j ++ ) {vis[i * prime[j]] = 1;if( i % prime[j] == 0 ) break;}}
}void build() {for( int k = 1;k <= cnt;k ++ )for( int j = 1;j < 20;j ++ )for( int i = 1;i + ( 1 << j ) - 1 <= n;i ++ )st[k][i][j] = max( st[k][i][j - 1], st[k][i + ( 1 << j - 1 )][j - 1] );
}int query( int k, int l, int r ) {int i = log2( r - l + 1 );return max( st[k][l][i], st[k][r - ( 1 << i ) + 1][i] );
}void build( int &now, int l, int r ) {if( ! now ) now = ++ tot;t[now].val = 1;if( l == r ) return;int mid = l + r >> 1;build( t[now].l, l, mid );build( t[now].r, mid + 1, r );
}void modify( int now, int l, int r, int pos, int val ) {if( l == r ) { t[now].val = val; return; }int mid = l + r >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos, val );else modify( t[now].r, mid + 1, r, pos, val );t[now].val = 1ll * t[t[now].l].val * t[t[now].r].val % mod;
}void modify( int lst, int &now, int l, int r, int pos, int val ) {if( ! now ) now = ++ tot;if( l == r ) { t[now].val = val; return; }int mid = l + r >> 1;if( pos <= mid ) {t[now].r = t[lst].r;modify( t[lst].l, t[now].l, l, mid, pos, val );}else {t[now].l = t[lst].l;modify( t[lst].r, t[now].r, mid + 1, r, pos, val );}t[now].val = 1ll * t[t[now].l].val * t[t[now].r].val % mod;
}int query( int now, int l, int r, int L, int R ) {if( ! now or r < L or R < l ) return 1;if( L <= l and r <= R ) return t[now].val;int mid = ( l + r ) >> 1;return 1ll * query( t[now].l, l, mid, L, R ) * query( t[now].r, mid + 1, r, L, R ) % mod;
}int main() {sieve();scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );for( int j = 1;j <= cnt;j ++ )if( a[i] % prime[j] == 0 )while( a[i] % prime[j] == 0 )st[j][i][0] ++, a[i] /= prime[j];}build();build( rt[0], 1, n );for( int i = 1;i <= n;i ++ ) {if( ! pos[a[i]] ) modify( rt[0], 1, n, i, a[i] );else nxt[pos[a[i]]] = i;pos[a[i]] = i;}for( int i = 1;i <= n;i ++ )if( nxt[i] ) modify( rt[i - 1], rt[i], 1, n, nxt[i], a[nxt[i]] );else rt[i] = rt[i - 1];for( int i = 1;i <= cnt;i ++ ) {mi[i][0] = 1;for( int j = 1;j < 20;j ++ )mi[i][j] = 1ll * mi[i][j - 1] * prime[i] % mod;}scanf( "%d", &Q );int lst = 0, l, r;while( Q -- ) {scanf( "%d %d", &l, &r );l = ( l + lst ) % n + 1;r = ( r + lst ) % n + 1;if( l > r ) swap( l, r );int ans = 1;for( int i = 1;i <= cnt;i ++ )ans = 1ll * ans * mi[i][query( i, l, r )] % mod;ans = 1ll * ans * query( rt[l - 1], 1, n, l, r ) % mod;printf( "%d\n", lst = ans );}return 0;
}
Colorful Squares
CodeForces-gym102979-C
二分正方形边长,转化为判断性问题
考虑扫描线
对于大小为ddd的正方形,先把xi=xx_i=xxi=x的点加入,再在x=xi+d+1x=x_i+d+1x=xi+d+1的时候删除
最后判断集合中是否存在一个yyy,满足[y−d,y][y-d,y][y−d,y]内的不同颜色种类数达到kkk
现在先加一个点[x0,y0][x_0,y_0][x0,y0]进集合,那么[y0−d,y][y_0-d,y][y0−d,y]这个区间内就有c0c_0c0这种颜色
设tit_iti :y=iy=iy=i时被多少种不同颜色覆盖
- 加一个点[x0,y0][x_0,y_0][x0,y0]对应的就是[y0−d,y][y_0-d,y][y0−d,y]的区间加
- 删除一个点[x0,y0][x_0,y_0][x0,y0]就是区间减
- 判断是否存在kkk种颜色就是区间max\rm maxmax是否等于kkk
当然可能[y0−d,y][y_0-d,y][y0−d,y]这个区间之前已经被c0c_0c0颜色覆盖过一部分,对于之前被覆盖过的区间就不需要操作
所以区间修改前还要调整计算真正的区间
这个调整不会很复杂
因为没被覆盖的区间不会被覆盖过的区间拆分开,没被覆盖的区间一定是完整的一段
#include <set>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 250005
#define lson num << 1
#define rson num << 1 | 1
struct tree { int Max, tag; }t[maxn << 2]; //线段树维护区间[l,r]出现不同颜色的种类数
vector < pair < int, int > > pos[maxn]; //横坐标为x[i]的点集合
multiset < int > col[maxn]; //颜色为c[i]的点的y坐标集合
int n, k;void pushdown( int num ) {t[lson].Max += t[num].tag;t[lson].tag += t[num].tag;t[rson].Max += t[num].tag;t[rson].tag += t[num].tag;t[num].tag = 0;
}void build( int num, int l, int r ) {t[num].Max = t[num].tag = 0;if( l == r ) return;int mid = l + r >> 1;build( lson, l, mid );build( rson, mid + 1, r );
}void modify( int num, int l, int r, int L, int R, int x ) {if( R < L or R < l or r < L ) return;if( L <= l and r <= R ) { t[num].Max += x, t[num].tag += x; return; }pushdown( num );int mid = l + r >> 1;modify( lson, l, mid, L, R, x );modify( rson, mid + 1, r, L, R, x );t[num].Max = max( t[lson].Max, t[rson].Max );
}bool check( int d ) {for( int i = 0;i <= n;i ++ ) col[i].clear();build( 1, 1, n );int L, R;for( int i = 1;i <= n;i ++ ) {for( auto now : pos[i] ) {//枚举横坐标为i的所有点int y = now.first, c = now.second;/*now点覆盖的y坐标为[y-d,y]但是这段区间中不能和重复出现过的颜色区间有交e.g. 颜色1 覆盖了[2,4] 又有颜色1能覆盖[3,5] 那么只能对[5,5]进行线段树加上颜色1的操作因为边长都是d 所以覆盖区间不可能一长一短导致区间分裂 每个点影响的区间都是一段完整不分裂的区间 */if( col[c].find( y ) != col[c].end() ) {//如果颜色c在纵坐标y的一条横扫描线上出现过 不再加入 col[c].insert( y );continue;}else col[c].insert( y );//[L,R]表示颜色c的now点真正贡献的区间 接下来就是求解范围限制 auto l = col[c].lower_bound( y );//找到颜色c中第一个大于等于y的点位置的纵坐标 if( l == col[c].begin() ) L = max( 1, y - d );//没有比现在now的纵坐标更小的点了 下限可以取完 防止溢出1 else L = max( *( --l ) + 1, y - d ); //--l第一个小于y的点位置纵坐标+1和覆盖左端点取max 避免重复 auto r = col[c].upper_bound( y );//找到颜色中第一个大于y的点 if( r == col[c].end() ) R = y;//没有比现在now点纵坐标更大的点了 else R = min( *r - d - 1, y );//点的覆盖区间有没有包含现在的now点 //要保证覆盖区间是合法的L<=R 这个可以在线段树最开始的时候判断 modify( 1, 1, n, L, R, 1 );}if( i - d - 1 > 0 ) {for( auto now : pos[i - d - 1] ) {//删掉与现在横坐标距离>d的点 因为是一个一个枚举 每次只用删去距离恰好为d+1的点 更小x的点肯定在前面就被其它的i'删掉了 int y = now.first, c = now.second;col[c].erase( col[c].find( y ) );if( col[c].find( y ) != col[c].end() ) continue;//如果纵坐标y这里还有点 那么此时删去的点对线段树内值不会造成影响//否则就同样的找到颜色c的这个now点真正贡献的区间auto l = col[c].lower_bound( y );if( l == col[c].begin() ) L = max( 1, y - d );else L = max( *( --l ) + 1, y - d );auto r = col[c].upper_bound( y );if( r == col[c].end() ) R = y;else R = min( *r - d - 1, y );modify( 1, 1, n, L, R, -1 );}}if( t[1].Max == k ) return 1;}return 0;
}int main() {scanf( "%d %d", &n, &k );for( int i = 1, x, y, c;i <= n;i ++ ) {scanf( "%d %d %d", &x, &y, &c );pos[x].push_back( make_pair( y, c ) );}n = maxn - 5;int l = 0, r = n, ans;while( l <= r ) {int mid = l + r >> 1;if( check( mid ) ) ans = mid, r = mid - 1;else l = mid + 1;}printf( "%d\n", ans );return 0;
}
线段树/扫描线问卷调查反馈——Rmq Problem / mex(主席树),Boring Queries(二分+st表+主席树),Colorful Squares(扫描线)相关推荐
- CF1422F Boring Queries(ST表 + 主席树)
CF1422F Boring Queries 给定一个长度为nnn的数组a,(1≤ai≤2×105)a,(1 \leq a_i \leq 2 \times 10 ^ 5)a,(1≤ai≤2×105) ...
- BZOJ3473:字符串(后缀数组,主席树,二分,ST表)
Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...
- 【st表/猫树】【堆+贪心】超级钢琴
[描述] 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中A ...
- 【JSOI2016】【st表/猫树】【枚举】灯塔
[题目描述] [思路] 这道题也很不错.首先这道题有O(nlogn)O(n\log n)O(nlogn)做法,但是我不会,我就只会O(nn)O(n \sqrt n)O(nn)+卡常的做法.这道题唯 ...
- P4137 Rmq Problem / mex 主席树求mex
传送门 文章目录 题意: 思路: 题意: 思路: 按照值建线段树,每个位置维护值出现的最后位置,让后可持久化一下,当查询[l,r][l,r][l,r]的时候,我们只需要在[1,r][1,r][1,r] ...
- 【BZOJ 3339 / BZOJ 3585 / luogu 4137】Rmq Problem / mex
[原题题面]传送门 [题解大意] 都说了是莫队练习题. 考虑已知[l,r]区间的mex值时,如何求[l+1,r]的mex值. 比较a[l+1]与已知ans的大小,如果a[l+1]>ans或者a[ ...
- BZOJ3166 [Heoi2013]Alo 【可持久化trie树 + 二分 + ST表】
题目 Welcome to ALO ( Arithmetic and Logistic Online).这是一个VR MMORPG , 如名字所见,到处充满了数学的谜题. 现在你拥有n颗宝石,每颗宝石 ...
- 19.CF803G Periodic RMQ Problem 线段树+分块+线段树标记
19.CF803G Periodic RMQ Problem 线段树+分块+线段树标记 个人Limitの线段树题单题解主目录:Limitの线段树题单 题解目录_HeartFireY的博客-CSDN博客 ...
- CodeForces - 1422F Boring Queries(主席树+线段树/RMQ)
题目链接:点击查看 题目大意:给出 n 个数组成的数组 a ,再给出 m 次询问,每次询问需要回答区间 [ l , r ] 中所有元素的最小公倍数,强制在线 题目分析:首先考虑多个数的最小公倍数该如何 ...
最新文章
- 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录
- 机器学习算法选择——特征提取
- 7-37 模拟EXCEL排序 (25 分)(思路+详解+超时解决 兄弟们冲呀呀呀呀呀呀)
- mysql 完整性概念_mysql基础知识
- 「管理数学基础」4.2 模糊数学:扩张原理、模糊数、可能性分布与模糊概率
- 如何将安卓设备连接到Mac电脑上?
- VB2010(24)窗体用户控件
- 用DDA算法绘制一条直线
- 西安工业大学计算机考研分数线,西安工业大学研究生录取分数线
- 购买装备(“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛
- 微信公众平台三方授权登录(Java实现)
- 黑苹果10.15.7使用
- 阿里云虚拟机多域名配置
- 编程小白的计算机毕业设计指导开发教程-javaweb i18n国际化的使用
- 盘古开源解析:数据防泄漏对于数据安全的重要性
- 大神解读:谷歌 ARCore 就是低配版 Tango,它比苹果 ARKit 好在哪?
- python中class什么意思_python-classmethod在此代码中做什么?
- ASUS 华硕 NX580V 飞行堡垒 安装WIN7系统教程
- mongodb数据库优缺点分析(扫盲)
- php判断caj文件页数,2个免费CAJ转PDF的方法,而且不限页数和大小
热门文章
- IT人喝酒,不同岗位不同姿势
- php生成vcf,详解PHP如何实现生成vcf vcard文件
- php+转义实体字符,PHP针对HTML实体字符的转义函数
- mysql root密码忘记2018_2018-03-28设置及修改mysql用户密码学习笔记
- c语言分配多一个字符空间,关于C语言动态给字符串分配内存空间问题
- c语言哈密顿路径算法,用于检查给定图中是否存在哈密顿循环或路径的C ++程序...
- python编写程序计算1+2+3+......+100和_Python3:计算两个列表总和为100的所有排列的最有效方法是什么?...
- 中南民族大学计算机图像处理实验报告,中南民族大学数字图像处理程序及图像...
- 量子计算机与新型传感器,新型量子传感器为超导量子计算机发展开辟了新路径...
- wordpress多站点主站调用分站最新文章_企业网站SEO最新的7个优化步骤!