长春赛的 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)—— 主席树初探的更多相关文章

  1. codeforces gym #101161E - ACM Tax(lca+主席树)

    题目链接: http://codeforces.com/gym/101161/attachments 题意: 给出节点数为$n$的树 有$q$次询问,输出$a$节点到$b$节点路程中,经过的边的中位数 ...

  2. 主席树初探--BZOJ1901: Zju2112 Dynamic Rankings

    n<=10000的序列做m<=10000个操作:单点修改,查区间第k小. 所谓的主席树也就是一个值域线段树嘛..不过在这里还是%%fotile 需要做一个区间查询,由于查第k小,需要一些能 ...

  3. 主席树初探--BZOJ2588: Spoj 10628. Count on a tree

    n<=100000的点权树,有m<=100000个询问,每次问两个点间的第k小点权,保证有解,强制在线. 主席上树啦!类似于之前的序列不带修改询问的前缀表示法,现在只要把前缀当成某点到根的 ...

  4. 主席树初探--BZOJ3524: [Poi2014]Couriers

    n<=500000个数,m<=500000个询问,每次问区间里出现次数>(R-L+1)的数字是谁,没有输出0. 写了带修改发现不会不带修改了.... 不带修改的话,n个点,每个点表示 ...

  5. [ACM]Link-Cut Tree实现动态树初探

    动态树问题是指的一类问题,而不是具体指的某一种数据结构.它主要维护一个包含若干有根树的森林,实现对森林的修改和查询等. 实现动态树的数据结构据说主要有4种,Link-Cut Tree是其中的一种.Li ...

  6. 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 ...

  7. UPC 2224 / “浪潮杯”山东省第四届ACM大学生程序设计竞赛 1008 Boring Counting 主席树

    Problem H:Boring Counting Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/ ...

  8. acm 2015北京网络赛 F Couple Trees 主席树+树链剖分

    提交 题意:给了两棵树,他们的跟都是1,然后询问,u,v 表 示在第一棵树上在u点往根节点走 , 第二棵树在v点往根节点走,然后求他们能到达的最早的那个共同的点 解: 我们将第一棵树进行书链剖,然后第 ...

  9. 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)

    J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...

随机推荐

  1. 《深入实践C++模板编程》之一——Hello模板

    1.通过一个简单的例子来理解模板的用途: 模板为不同类型的数据生成操作相同或相似的函数. 弱语言如Python,可以使用一种函数来应对各种类型,但是C++就不得不为不同的类型编写相似的函数.模板的作用 ...

  2. vs2010 回车、退格键等不能用

    有时候在vs2010中,突然回退键.回车键.方向键就用不了了,百度一堆方法,最后找到按Alt+Enter,就可以用了.

  3. Flink概述

    计算引擎 大数据计算引擎分为离线计算和实时计算,离线计算就是我们通常说的批计算,代表是Hadoop MapReduce.Hive等大数据技术.实时计算也被称作流计算,代表是Storm.Spark St ...

  4. Bootstrap页面响应式设计

    关键词:viewport.栅格布局.媒体查询(Media Queries) 一.关于栅格布局的说明: 1.基本图解 extra small devices phones  超小型设备手机small d ...

  5. O060、Restore Volume 操作

    参考https://www.cnblogs.com/CloudMan6/p/5668872.html   前面我们学习了backup操作,现在我们来学习如何使用backup进行restore.   r ...

  6. hive用户自定义函数

    一.UDF 1.显示所有函数:show functions ; 2.显示指定函数的帮助:$hive>desc function current_database(); 3. 什么是 UDF? 当 ...

  7. R语言学习笔记:读取前n行数据

    常规读取 一般我们读取文件时都会读取全部的文件然后再进行操作,因为R是基于内存进行计算的. data <- read.table("C:\\Users\\Hider\\Desktop\ ...

  8. C# 移除数组中重复项

    方法一: static void Main(string[] args) { //看到数组的第一反应应该是排序 ,,,,,,,}; //去掉数组中重复的项 //先排序 arrayAsc(array); ...

  9. Java高并发程序设计学习笔记(八):NIO和AIO

    转自:https://blog.csdn.net/dataiyangu/article/details/87214773 什么是NIOBuffer && ChannelBuffer举个 ...

  10. <%%> <%! %> <%=%> <%-- --%> jsp中jstl一些运用

    <%%> 这里面可以添加java代码片段<%! %> 这里添加java方法 主要是用来声明变量的 <%=%> 将变量或表达式值输出到页面<%-- --%> ...