ACM之路(19)—— 主席树初探
长春赛的 I 题是主席树,现在稍微的学了一点主席树,也就算入了个门吧= =
简单的来说主席树就是每个节点上面都是一棵线段树,但是这么多线段树会MLE吧?其实我们解决的办法就是有重复的节点给他利用起来,具体见幻神博客。
不妨以1~n上的求任意区间第k小的问题,就是上面博客中所写,我们从1访问到n的预处理中,每一个时间都新建一个线段树,这棵树上记录着已经出现的各个数字,这样我们求[L,R]上的第k小,我们拿R时刻的线段树减去(L-1)时刻的线段树,就是这个区间内需要的线段树,这个线段树上存在的数字其实就是[L,R]上存在的数字,我们在这里寻找我们需要的第k小就可以了。具体实现方法见上面的博客。
我自己的模板如下:
- #include <stdio.h>
- #include <algorithm>
- #include <string.h>
- #define t_mid (l+r>>1)
- using namespace std;
- const int N = + ;
- int n,q,tot,sz;
- int a[N],b[N];
- int rt[N*],sum[N*],ls[N*],rs[N*];
- void build(int &o,int l,int r)
- {
- o = ++tot;
- sum[o] = ;
- if(l==r) return;
- build(ls[o],l,t_mid);
- build(rs[o],t_mid+,r);
- }
- void update(int &o,int l,int r,int last,int p)
- {
- o = ++tot;
- ls[o] = ls[last];
- rs[o] = rs[last];
- sum[o] = sum[last] + ;
- if(l==r) return;
- if(p <= t_mid) update(ls[o],l,t_mid,ls[last],p);
- else update(rs[o],t_mid+,r,rs[last],p);
- }
- int query(int ql,int qr,int l,int r,int k)
- {
- if(l==r) return l;
- int cnt = sum[ls[qr]] - sum[ls[ql]];
- if(cnt >= k) return query(ls[ql],ls[qr],l,t_mid,k);
- else return query(rs[ql],rs[qr],t_mid+,r,k-cnt);
- }
- void work()
- {
- int ql,qr,k;
- scanf("%d%d%d",&ql,&qr,&k);
- int ans = query(rt[ql-],rt[qr],,sz,k);
- printf("%d\n",b[ans]);
- }
- int main()
- {
- while(scanf("%d%d",&n,&q)==)
- {
- tot = ;
- for(int i=;i<=n;i++) scanf("%d",a+i),b[i]=a[i];
- sort(b+,b++n);
- sz = unique(b+,b++n) - (b+);
- build(rt[],,sz);
- for(int i=;i<=n;i++)
- {
- int t = lower_bound(b+,b++sz,a[i]) - b;
- update(rt[i],,sz,rt[i-],t);
- }
- while(q--) work();
- }
- }
求区间第K小
然后如果是在一棵树上,求其一条链上的区间第k小呢?其实也差不多,我们就想着怎么把这棵需要的线段树抽取出来就行。这棵树实际上就是 u - lca(u,v) + v - father(lca(u,v))。具体的画画图就可以懂了。这里还涉及到求LCA的方法,具体方法见《挑战程序设计》中的倍增法求LCA即可。
我自己的模板如下:
- #include <stdio.h>
- #include <algorithm>
- #include <string.h>
- #include <vector>
- #include <math.h>
- #define t_mid (l+r>>1)
- using namespace std;
- const int N = + ;
- const int MAX_LOG_N = + ;
- int n,q,tot,sz;
- int a[N],b[N];
- int rt[N*],sum[N*],ls[N*],rs[N*];
- int parent[MAX_LOG_N][N],depth[N];
- vector<int> G[N];
- void getDepth(int v,int p,int d)
- {
- parent[][v] = p;
- depth[v] = d;
- for(int i=;i<G[v].size();i++)
- {
- if(G[v][i] != p) getDepth(G[v][i],v,d+);
- }
- }
- void init()
- {
- getDepth(,-,);
- for(int k=;k+<MAX_LOG_N;k++)
- {
- for(int v=;v<=n;v++)
- {
- if(parent[k][v] < ) parent[k+][v] = -;
- else parent[k+][v] = parent[k][parent[k][v]];
- }
- }
- }
- int lca(int u,int v)
- {
- if(depth[u]>depth[v]) swap(u,v);
- for(int k=;k<MAX_LOG_N;k++)
- {
- if((depth[v]-depth[u]) >> k & )
- {
- v = parent[k][v];
- }
- }
- if(u==v) return u;
- for(int k=MAX_LOG_N-;k>=;k--)
- {
- if(parent[k][u] != parent[k][v])
- {
- u = parent[k][u];
- v = parent[k][v];
- }
- }
- return parent[][u];
- }
- void build(int &o,int l,int r)
- {
- o = ++tot;
- sum[o] = ;
- if(l==r) return;
- build(ls[o],l,t_mid);
- build(rs[o],t_mid+,r);
- }
- void update(int &o,int l,int r,int last,int p)
- {
- o = ++tot;
- ls[o] = ls[last];
- rs[o] = rs[last];
- sum[o] = sum[last] + ;
- if(l==r) return;
- if(p <= t_mid) update(ls[o],l,t_mid,ls[last],p);
- else update(rs[o],t_mid+,r,rs[last],p);
- }
- int query(int u,int v,int x,int y,int l,int r,int k)
- {
- if(l==r) return l;
- int cnt = sum[ls[u]] + sum[ls[v]] - sum[ls[x]] - sum[ls[y]];
- if(cnt >= k) return query(ls[u],ls[v],ls[x],ls[y],l,t_mid,k);
- else return query(rs[u],rs[v],rs[x],rs[y],t_mid+,r,k-cnt);
- }
- void work()
- {
- int u,v,k;
- scanf("%d%d%d",&u,&v,&k);
- int _lca = lca(u,v);
- int _lca_fa = parent[][_lca];
- int ans = query(rt[u],rt[v],rt[_lca],rt[_lca_fa],,sz,k);
- printf("%d\n",b[ans]);
- }
- void dfs(int u,int fa)
- {
- for(int i=;i<G[u].size();i++)
- {
- int v = G[u][i];
- if(v==fa) continue;
- int t = lower_bound(b+,b++sz,a[v]) - b;
- update(rt[v],,sz,rt[u],t);
- dfs(v,u);
- }
- }
- int main()
- {
- while(scanf("%d%d",&n,&q)==)
- {
- tot = ;
- for(int i=;i<=n;i++) G[i].clear();
- for(int i=;i<=n;i++) scanf("%d",a+i),b[i]=a[i];
- sort(b+,b++n);
- sz = unique(b+,b++n) - (b+);
- for(int i=;i<n;i++)
- {
- int u,v;scanf("%d%d",&u,&v);
- G[u].push_back(v);
- G[v].push_back(u);
- }
- build(rt[],,sz);
- init();
- int t = lower_bound(b+,b++sz,a[]) - b;
- update(rt[],,sz,rt[],t);
- dfs(,-);
- while(q--) work();
- }
- }
求树上的一条链的第K小
好,接下来就是解决那个烦人的 I 题了。
我们首先需要用主席树来解决区间内不同的数的个数,这东西比较奥义- -直接上模板好了。。反正随便百度一下"主席树求区间内不同数的个数"都会出来spoj的D-query那题,随便看下原理就行= =。。。然后用二分解决 I 题(固定左端点,二分右端点,具体见代码。。)。
看我直接丢 I 题的代码~:
- #include <stdio.h>
- #include <algorithm>
- #include <string.h>
- #include <map>
- #define t_mid (l+r>>1)
- using namespace std;
- const int N = * + ;
- int rt[N**],sum[N**],ls[N**],rs[N**];
- int a[N],n,m,tot;
- void build(int &o,int l,int r)
- {
- o = ++tot;
- sum[o] = ;
- if(l == r) return;
- build(ls[o],l,t_mid);
- build(rs[o],t_mid+,r);
- }
- void update(int &o,int l,int r,int last,int pos,int dt)
- {
- o = ++tot;
- sum[o] = sum[last];
- ls[o] = ls[last];
- rs[o] = rs[last];
- if(l==r) {sum[o]+=dt;return;}
- if(pos <= t_mid) update(ls[o],l,t_mid,ls[last],pos,dt);
- else update(rs[o],t_mid+,r,rs[last],pos,dt);
- sum[o] = sum[ls[o]] + sum[rs[o]];
- }
- int query(int l,int r,int o,int pos)
- {
- if(l == r) return sum[o];
- if(pos <= t_mid) return sum[rs[o]] + query(l,t_mid,ls[o],pos);
- else return query(t_mid+,r,rs[o],pos);
- }
- /*
- int query(int l,int r,int L,int R,int x){
- if(L <= l && r <= R) return sum[x];
- int mid = (l+r) >> 1 , ret = 0;
- if(L <= mid) ret += query(l,mid,L,R,ls[x]);
- if(R > mid) ret += query(mid+1,r,L,R,rs[x]);
- return ret;
- }
- */
- int main()
- {
- int T;scanf("%d",&T);
- for(int kase=;kase<=T;kase++)
- {
- scanf("%d%d",&n,&m);
- int pre = ;
- map<int,int> mp;
- tot = ;
- for(int i=;i<=n;i++) scanf("%d",a+i);
- build(rt[],,n);
- for(int i=;i<=n;i++)
- {
- if(mp.find(a[i]) == mp.end())
- {
- mp[a[i]] = i;
- update(rt[i],,n,rt[i-],i,);
- }
- else
- {
- int temp = ;
- update(temp,,n,rt[i-],mp[a[i]],-);
- update(rt[i],,n,temp,i,);
- }
- mp[a[i]] = i;
- }
- //scanf("%d",&m);
- printf("Case #%d:",kase);
- while(m--)
- {
- int ql,qr;scanf("%d%d",&ql,&qr);
- int L = min((ql+pre)%n+,(qr+pre)%n+);
- int R = max((ql+pre)%n+,(qr+pre)%n+);
- //L = ql, R = qr;
- int k = (query(,n,rt[R],L)+)>>;
- int l = L, r = R;
- //printf("!! %d %d \n",L,R);
- int ans = -;
- while(l<=r)
- {
- int mid = l + r >> ;
- int t = query(,n,rt[mid],L);
- //printf("mid is %d %d\n",mid,t);
- if(t < k) l = mid + ;
- else
- {
- r = mid - ;
- ans = mid;
- }
- }
- /*while(l < r)
- {
- int mid = l + r >> 1;
- int t = query(1,n,rt[mid],L);
- if(t < k) l = mid + 1;
- else r = mid;
- }*/
- printf(" %d",ans);
- pre = ans;
- }
- puts("");
- }
- }
- /*
- 100
- 20 100
- 1 2 3 4 3 2 1 2 4 2 2 3 1 2 3 1 4 4 2 1
- 1 20
- 1 10
- 2 5
- 4 6
- 3 2
- 4 7
- 100
- 5 100
- 0 1 0 2 3
- 1 5
- */
- /*
- #include<iostream>
- //#include<bits/stdc++.h>
- #include<cstdio>
- #include<string>
- #include<cstring>
- #include<map>
- #include<queue>
- #include<set>
- #include<stack>
- #include<ctime>
- #include<algorithm>
- #include<cmath>
- #include<vector>
- #define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
- //#pragma comment(linker, "/STACK:1024000000,1024000000")
- using namespace std;
- typedef long long ll;
- typedef long long LL;
- #define MP make_pair
- #define PII pair<int,int>
- #define PLI pair<long long ,int>
- #define PFI pair<double,int>
- #define PLL pair<ll,ll>
- #define PB push_back
- #define F first
- #define S second
- #define lson l,mid,rt<<1
- #define rson mid+1,r,rt<<1|1
- #define debug cout<<"?????"<<endl;
- //freopen("1005.in","r",stdin);
- //freopen("data.out","w",stdout);
- const int INF = 0x3f3f3f3f;
- const double eps = 1e-2;
- const int N = 4e5 + 50 ;
- const double PI = acos(-1.);
- const double E = 2.71828182845904523536;
- const int MOD = 1e9+7;
- typedef vector<ll> Vec;
- typedef vector<Vec> Mat;
- int n,m;
- struct node{int l,r,sum;}T[N*40];
- int a[N],root[N],pre[N],tot;
- int q,x,y;
- int ans[N];
- vector<int> v;
- int getid(int x){ return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;}
- void init(){
- tot = 0;
- memset(root,0,sizeof(root));
- memset(pre,-1,sizeof(pre));
- v.clear();
- }
- void update(int l,int r,int val,int &x,int y,int pos){
- T[++tot] = T[y] , T[tot].sum += val , x = tot;
- if(l == r) return ;
- int mid = (l + r) >> 1;
- if(pos <= mid) update(l,mid,val,T[x].l,T[y].l,pos);
- else update(mid+1,r,val,T[x].r,T[y].r,pos);
- }
- **
- * 【x=L,y=R】 不同数字的有多少个
- * query(1,n,x,y,root[y]); 第y颗树。
- *
- int query(int l,int r,int L,int R,int x){
- if(L <= l && r <= R) return T[x].sum;
- int mid = (l+r) >> 1 , ret = 0;
- if(L <= mid) ret += query(l,mid,L,R,T[x].l);
- if(R > mid) ret += query(mid+1,r,L,R,T[x].r);
- return ret;
- }
- int main(){
- int kase = 1,T;
- cin >> T;
- while(T --){
- cin >> n >> m;
- init();
- for(int i = 1 ; i <= n ; i ++) scanf("%d",&a[i]) , v.push_back(a[i]);
- sort(v.begin(),v.end());
- v.erase(unique(v.begin(),v.end()),v.end());
- for(int i = 1 ; i <= n ; i ++){
- int id = getid(a[i]);
- if(pre[id] == -1){
- update(1,n,1,root[i],root[i-1],i);
- pre[id] = i;
- }else{
- int tmp;
- update(1,n,-1,tmp,root[i-1],pre[id]);
- update(1,n,1,root[i],tmp,i);
- pre[id] = i;
- }
- }
- ans[0] = 0;
- printf("Case #%d:",kase ++);
- for(int i = 1 ; i <= m ; i ++){
- scanf("%d%d",&x,&y);
- int l,r;
- l = min((x+ans[i-1])%n+1,(y+ans[i-1])%n+1);
- r = max((x+ans[i-1])%n+1,(y+ans[i-1])%n+1);
- //l = x ; r = y;
- //printf("%d %d !!\n",l,r);
- int k = (query(1,n,l,r,root[r])+1) / 2;
- int ll = l , rr = r;
- while(ll < rr){
- int mid = (ll + rr) / 2;
- int t = query(1,n,l,mid,root[mid]);
- if(t < k) ll = mid+1;
- else rr = mid;
- }
- printf(" %d",rr);
- ans[i] = rr;
- }
- puts("");
- }
- return 0;
- }
- */
长春 I 题
有几点想说明的:1.下面注释的是大力的代码,但是超时了,因为他的query方法和我的有点小差别,虽然都能实现需要的功能,但是似乎我的query方法复杂度更小一点(??)。。不过我的也是卡过的,但是我觉得在长春现场赛的话应该能过,感觉HDU的评测机这次有点坑。。2.我的代码本来是WA的,因为数组开小了,我上面的两个代码都是*20的,都没问题,这里必须要开*40的才行,被坑了这一次以后我下次都开大一点的好了,反正*40内存也够用= =。。那么主席树就写到这里好了,以后刷了题目有什么要补充的再补充好了~(话说我的数据结构真的好烂啊,,以后搞splay怎么办啊233。。)
ACM之路(19)—— 主席树初探的更多相关文章
- codeforces gym #101161E - ACM Tax(lca+主席树)
题目链接: http://codeforces.com/gym/101161/attachments 题意: 给出节点数为$n$的树 有$q$次询问,输出$a$节点到$b$节点路程中,经过的边的中位数 ...
- 主席树初探--BZOJ1901: Zju2112 Dynamic Rankings
n<=10000的序列做m<=10000个操作:单点修改,查区间第k小. 所谓的主席树也就是一个值域线段树嘛..不过在这里还是%%fotile 需要做一个区间查询,由于查第k小,需要一些能 ...
- 主席树初探--BZOJ2588: Spoj 10628. Count on a tree
n<=100000的点权树,有m<=100000个询问,每次问两个点间的第k小点权,保证有解,强制在线. 主席上树啦!类似于之前的序列不带修改询问的前缀表示法,现在只要把前缀当成某点到根的 ...
- 主席树初探--BZOJ3524: [Poi2014]Couriers
n<=500000个数,m<=500000个询问,每次问区间里出现次数>(R-L+1)的数字是谁,没有输出0. 写了带修改发现不会不带修改了.... 不带修改的话,n个点,每个点表示 ...
- [ACM]Link-Cut Tree实现动态树初探
动态树问题是指的一类问题,而不是具体指的某一种数据结构.它主要维护一个包含若干有根树的森林,实现对森林的修改和查询等. 实现动态树的数据结构据说主要有4种,Link-Cut Tree是其中的一种.Li ...
- HDU 4729 An Easy Problem for Elfness(主席树)(2013 ACM/ICPC Asia Regional Chengdu Online)
Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans to build a huge wat ...
- UPC 2224 / “浪潮杯”山东省第四届ACM大学生程序设计竞赛 1008 Boring Counting 主席树
Problem H:Boring Counting Time Limit : 6000/3000ms (Java/Other) Memory Limit : 65535/32768K (Java/ ...
- acm 2015北京网络赛 F Couple Trees 主席树+树链剖分
提交 题意:给了两棵树,他们的跟都是1,然后询问,u,v 表 示在第一棵树上在u点往根节点走 , 第二棵树在v点往根节点走,然后求他们能到达的最早的那个共同的点 解: 我们将第一棵树进行书链剖,然后第 ...
- 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)
J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...
随机推荐
- 服务端相关知识学习(二)之Zookeeper可以干什么
Zookeeper主要可以干哪些事情 配置管理,名字服务,提供分布式同步以及集群管理.那这些服务又到底是什么呢?我们为什么需要这样的服务?我们又为什么要使用Zookeeper来实现呢,使用Zookee ...
- Linux学习笔记:7个ssh命令用法
通过远程控制管理多台服务器. 远程工具:telnet.ssh.vnc ssh采用密文的传输方式,简单安全.Secure Shell 缩写 SSH. 1.基本用法 ssh 192.168.1.1 默认使 ...
- javascript框架(库)
javascript框架(库)高级JavaScript编程,尤其是复杂的浏览器差异处理,通常是困难和耗时的.为了响应这些调整,出现了许多javascript(helper)库.这些JavaScript ...
- Nginx作为静态资源web服务之缓存原理
Nginx作为静态资源web服务之缓存原理 大致理一下http浏览器缓存原理: 浏览器第一次请求服务器,此时浏览器肯定没有缓存,则直接调用服务器端,服务器在返回的信息的信息头中添加 ETag和Last ...
- layui弹出层基础参数
一.type-层类型 类型:Number 默认为0(信息框); 1(页面层),可以在页面添加HTML内容 2(iframe层) 3(加载层)加载时显示的弹出框 4(tips层) 需要绑定ID就不展示 ...
- Delphi CreateFile函数
- javascript 元编程之-代码修改代码
javascript 元编程之-代码修改代码 引言 重构代码是个体力活,特别是在确定重构方案后,剩下就是按方案调整代码,然后进行测试. 如何有好又快的调整到位代码,这是件不容易的事. 简单的代码,可以 ...
- systemd自启动tomcat
tomcat自启动service [Unit] Description=Tomcat After=network.target [Service] Type=forking PIDFile=/usr/ ...
- bug的全部
BUG 的生命周期 BUG 的生命周期 Bug-->软件程序的漏洞或缺陷 Bug 的类型:代码错误.设计缺陷.界面优化.性能问题.配置相关.安装部署.安全相关.标准规划.测试脚本....其他(功 ...
- kotlin的loop和Range、list和map
继续学习Kolin的基础语法,比较简单,直接练习代码.loop和range: 这里用一个场景来说明:计算从1到100之间数的总和,那在kotlin中是如何搞的呢? 上面这么简单的一句代码确实是能表达么 ...