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* ...
随机推荐
- 数据结构-二叉搜索树Java实现
1,Node.java 生成基础二叉树的结构 package com.cnblogs.mufasa.searchTree; /** * 节点配置父+左+右 */ public class Node{ ...
- whistle学习(二)之启动、停止、重启、更新whistle等命令
新版本的whistle支持三种等价命令whistle,w2,wproxy 启动whistle w2 start 启动时指定端口 w2 start -p (// 不设置端口默认使用8899) 默认端口为 ...
- SVN客户端(TortoiseSVN)保存密码自动登录后,如何切换使用其它帐户登录方法
清除SVN客户端(TortoiseSVN)保存的认证信息(用户名和密码) 1.选择TortoiseSVN---->Settings. 2.点"Clear” ,清空Authenticat ...
- href="javascript:show_login()"意思
整句话意味着当你点击一个超链接时,你会触发函数show_login. Href是一个超链接,通过单击该超链接触发. javascript:后面是JS代码 show_login():表示JS的函数的油烟 ...
- Vue-Cli项目如何查看依赖调用关系?
Vue是个优秀的前端框架,不管是前端还是后端开发人员都能很快使用Vue来开发应用.但是随着项目开发的深入,组件之间的依赖就变得越来越多,耦合越来越严重.这时候我们迫切地需要分析下组件和依赖之间的调用关 ...
- JavaMaven【一、概述&环境搭建】
课程概述 JavaMaven[一.概述&环境搭建] JavaMaven[二.目录结构&HelloMaven] JavaMaven[三.常用指令] JavaMaven[四.坐标& ...
- PAT Basic 1092 最好吃的月饼 (20 分)
月饼是久负盛名的中国传统糕点之一,自唐朝以来,已经发展出几百品种. 若想评比出一种“最好吃”的月饼,那势必在吃货界引发一场腥风血雨…… 在这里我们用数字说话,给出全国各地各种月饼的销量,要求你从中找出 ...
- php is_numeric函数可绕过产生SQL注入
老老实实mysql_real_escape_string()防作死......is_numeric的SQL利用条件虽然有点苛刻,但还是少用的好= = 某CTF中亦有实测案例,请戳 http://dro ...
- git fetch, merge, pull, push需要注意的地方
在git操作中,我们经常会用到fetch, merge, pull和push等命令,以下是一些我们需要注意的地方. 给大家准备了参考资料: 1. Whatʼs a Fast Forward Merge ...
- vs调试时,不显示局部变量
为了测试一个函数的返回值,就在某个函数里加了一个局部变量,调试却不显示所添加变量的信息. 你一定设置成了release 模式.改为debug就可以了. 比较弱智的问题,mark一下.