http://www.lydsy.com/JudgeOnline/problem.php?id=3224
基础的平衡树操作。对序列中单点修改、求第k小数、求某个数的排名、查询某个数的前驱后继。
#include
#include
#include
#include
#define MAXN 100010
#define lson node[o].lc
#define rson node[o].rc
using namespace std;
struct Treap
{
struct Node
{
int lc,rc;
int val,key; //val=该点的键值(平衡二叉树用),key=该点的权值(Heap用)
int cnt; //该点对应的相同数字个数
int size; //该点对应的子树大小
}node[MAXN];
int nCount,root; //结点总个数、根节点编号
void pushup(int o) //维护结点o
{
node[o].size=node[lson].size+node[rson].size+node[o].cnt;
}
void rotateR(int &o) //右旋结点o
{
int tmp=lson;
lson=node[tmp].rc;
node[tmp].rc=o;
node[tmp].size=node[o].size;
pushup(o);
o=tmp;
}
void rotateL(int &o) //左旋结点o
{
int tmp=rson;
rson=node[tmp].lc;
node[tmp].lc=o;
node[tmp].size=node[o].size;
pushup(o);
o=tmp;
}
void insert(int &o,int x) //在根节点为o的子树下插入键值为x的点
{
if(o==0) //空树,建立新的节点
{
o=++nCount;
node[o].size=node[o].cnt=1;
node[o].key=rand()*rand();
node[o].val=x;
return;
}
node[o].size++;
if(xnode[lson].key) //维护小根堆的性质
rotateR(o);
}
else if(x>node[o].val) //x比o的键值大,往右子树插入
{
insert(rson,x);
if(node[o].key>node[rson].key) //维护小根堆的性质
rotateL(o);
}
else //x与o的键值相同
node[o].cnt++;
}
void del(int &o,int x) //在根节点为o的子树下删除键值为x的点
{
if(o==0) return;
if(node[o].val==x) //o的键值就是x
{
if(node[o].cnt>1) //o对应的相同数字不止一个
{
node[o].size--;
node[o].cnt--;
}
//o只对应一个数字,删掉这个数字,结点o就没了
else if(lson==0||rson==0) //o只有一个儿子
o=lson+rson;
else if(node[lson].keynode[lson].size+node[o].cnt) return getNumFromRank(rson,kth-node[lson].size-node[o].cnt);
else return node[o].val;
}
void getBefore(int o,int x,int &ans) //在树o中求键值为x的结点的前驱
{
if(o==0) return;
if(node[o].valx)
{
ans=node[o].val;
getAfter(lson,x,ans);
}
else getAfter(rson,x,ans);
}
}treap;
int main()
{
srand(23333);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int cmd,x;
scanf("%d%d",&cmd,&x);
if(cmd==1) //插入操作
treap.insert(treap.root,x);
else if(cmd==2) //删除操作
treap.del(treap.root,x);
else if(cmd==3) //求排名
printf("%d\n",treap.getRank(treap.root,x));
else if(cmd==4) //已知排名求数字
printf("%d\n",treap.getNumFromRank(treap.root,x));
else if(cmd==5) //求前驱
{
int ans=0;
treap.getBefore(treap.root,x,ans);
printf("%d\n",ans);
}
else if(cmd==6) //求后继
{
int ans=0;
treap.getAfter(treap.root,x,ans);
printf("%d\n",ans);
}
}
return 0;
}
http://www.lydsy.com/JudgeOnline/problem.php?id=3223
要求支持对序列进行翻转操作,并输出最终的序列。给Splay打上翻转标记rev即可。建议将长度为n的序列1~n转变为长度为n+2的序列1~n+2,最左边的数字1和最右边的数字n+2起保护作用,真正的被操作序列是2~n+1。
#include
#include
#include
#include
#include
#define MAXN 100100
#define lson node[o].ch[0]
#define rson node[o].ch[1]
using namespace std;
struct Splay
{
struct node
{
int fa; //父结点
bool rev; //翻转标记
int size; //子树大小
int val; //结点键值
int ch[2]; //左右儿子
}node[MAXN];
int nCount,root; //开的结点个数、根节点
int id[MAXN]; //初始时的序列
void pushup(int o) //上传size标记
{
node[o].size=node[lson].size+node[rson].size+1;
}
void pushdown(int o) //下传rev标记
{
if(node[o].rev)
{
swap(lson,rson); //交换左右儿子
node[lson].rev^=1;
node[rson].rev^=1;
node[o].rev=0;
}
}
void build(int L,int R,int fa) //建立区间[L,R]的结点,该结点对应子树的父亲是fa
{
if(L>R) return;
int now=L,last=fa;
if(L==R)
{
node[L].fa=fa;
node[L].size=1;
node[L].val=id[L];
if(L>1;
build(L,M-1,M);
build(M+1,R,M);
node[M].fa=fa;
node[M].val=id[M];
pushup(M);
if(M=kth) return getKth(lson,kth);
else return getKth(rson,kth-node[lson].size-1);
}
int reverse(int L,int R) //翻转区间(L,R)
{
int x=getKth(root,L),y=getKth(root,R+2);
splay(x,root);
splay(y,node[x].ch[1]);
int z=node[y].ch[0]; //z子树对应区间(L,R)
node[z].rev^=1;
}
}spt;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n+2;i++)
spt.id[i]=i;
spt.build(1,n+2,0);
spt.root=(n+3)>>1;
for(int i=1;i<=m;i++)
{
int L,R;
scanf("%d%d",&L,&R);
spt.reverse(L,R);
}
for(int i=2;i<=n+1;i++)
printf("%d ",spt.getKth(spt.root,i)-1);
return 0;
}
http://www.lydsy.com/JudgeOnline/problem.php?id=1036
要求支持对一棵有点权、无边权的静态无根树进行修改点权操作,并支持查询任意两点之间最短路上的最大点权和点权和。
裸树链剖分+套线段树或者BIT。
#include
#include
#include
#include
#include
#define MAXN 31000
#define INF 0x3f3f3f3f
#define lowbit(x) ((x)&(-(x)))
using namespace std;
struct edge
{
int u,v,next;
}edges[MAXN*2];
int head[MAXN],nCount=0;
void AddEdge(int U,int V) //添加有向边U->V
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].next=head[U];
head[U]=nCount;
}
void add(int U,int V) //添加无向边U-V
{
AddEdge(U,V);
AddEdge(V,U);
}
int depth[MAXN],fa[MAXN],size[MAXN],son[MAXN]; //depth[i]=点i的深度,fa[i]=树中点i的父亲,size[i]=子树i的大小,son[i]=点i的重儿子
int hash[MAXN],num=0; //hash[i]=点i在线段树中的编号
int top[MAXN]; //top[i]=点i所属重链的祖先
int w[MAXN];
void DFS(int u,int dep) //第一次DFS找重儿子
{
size[u]=1;
son[u]=0;
depth[u]=dep;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(v==fa[u]) continue;
fa[v]=u;
DFS(v,dep+1);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void BuildTree(int u,int anc) //第二次DFS找重链
{
hash[u]=++num;
top[u]=anc;
if(son[u]) BuildTree(son[u],anc);
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(v==fa[u]||v==son[u]) continue;
BuildTree(v,v);
}
}
struct SegmentTree
{
int maxv[4*MAXN],sumv[4*MAXN];
void pushup(int o) //上传标记
{
sumv[o]=sumv[o*2]+sumv[o*2+1];
maxv[o]=max(maxv[o*2],maxv[o*2+1]);
}
void update(int pos,int x,int L,int R,int o) //当前结点o对应区间[L,R],将pos位置的数字修改为x
{
if(L==R)
{
maxv[o]=sumv[o]=x;
return;
}
int M=(L+R)>>1;
if(pos<=M) update(pos,x,L,M,o*2);
else update(pos,x,M+1,R,o*2+1);
pushup(o);
}
int QueryMax(int ql,int qr,int L,int R,int o) //求[ql,qr]最值
{
if(ql==L&&qr==R) return maxv[o];
int M=(L+R)>>1;
if(qr<=M) return QueryMax(ql,qr,L,M,o*2);
else if(ql>M) return QueryMax(ql,qr,M+1,R,o*2+1);
else return max(QueryMax(ql,M,L,M,o*2),QueryMax(M+1,qr,M+1,R,o*2+1));
}
int QuerySum(int ql,int qr,int L,int R,int o) //求[ql,qr]区间和
{
if(ql==L&&qr==R) return sumv[o];
int M=(L+R)>>1;
if(qr<=M) return QuerySum(ql,qr,L,M,o*2);
else if(ql>M) return QuerySum(ql,qr,M+1,R,o*2+1);
else return QuerySum(ql,M,L,M,o*2)+QuerySum(M+1,qr,M+1,R,o*2+1);
}
}segtree;
int QueryMax(int a,int b,int n) //求a到b的路径上的最大点权,整棵树有n个点
{
int ta=top[a],tb=top[b],ans=-INF;
while(ta!=tb)
{
if(depth[ta]depth[b]) swap(a,b);
return max(ans,segtree.QueryMax(hash[a],hash[b],1,n,1));
}
int QuerySum(int a,int b,int n) //求a到b的路径上的点权和,整棵树有n个点
{
int ta=top[a],tb=top[b],ans=0;
while(ta!=tb)
{
if(depth[ta]depth[b]) swap(a,b);
return ans+=segtree.QuerySum(hash[a],hash[b],1,n,1);
}
int main()
{
memset(head,-1,sizeof(head));
int n;
char cmd[10];
scanf("%d",&n);
for(int i=1;i
http://www.lydsy.com/JudgeOnline/problem.php?id=2631
一道很好的LCT练手题。注意打标记的问题,由于此题中同时存在区间乘和区间加标记,因此要注意如何更新这两个标记,我们可以约定同时存在区间乘和区间加标记时,先区间同时乘,然后再进行区间加。如果一个区间要乘上mulv后再加上addv,而其原有的区间和为sumv[o],原有的区间乘和区间加标记为mul[o]和add[o],那么此次修改操作后这个区间的区间和应为[(sumv[o]*mul[o]+add[o])*mulv+addv]=sumv[o]*(mul[o]*mulv)+(add[o]*mulv+addv),因此该区间新的区间乘标记为(mul[o]*mulv),新的区间加标记为(add[o]*mulv+addv)
#include
#include
#include
#include
#include
#define MAXN 102000
#define MOD 51061
#define lson ch[o][0]
#define rson ch[o][1]
using namespace std;
typedef unsigned int uint;
struct LinkCutTree
{
int nCount; //开的结点个数
int ch[MAXN][2],fa[MAXN]; //Splay的儿子标记
bool rev[MAXN]; //Splay的翻转标记
int size[MAXN]; //Splay的子树大小标记
uint sum[MAXN],mul[MAXN],add[MAXN],val[MAXN]; //Splay的区间和标记、区间乘法标记、区间加法标记、本节点的值
int q[MAXN],top; //伸展时用的栈
bool isRoot(int x) //判断x是否是Splay的根节点
{
return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void pushup(int o) //上传标记操作
{
sum[o]=sum[lson]+sum[rson]+val[o];
sum[o]%=MOD;
size[o]=size[lson]+size[rson]+1;
size[o]%=MOD; //????
}
void cal(int o,int mulv,int addv) //点o对应的区间先乘上mulv后加上addv
{
if(!o) return;
val[o]=(val[o]*mulv+addv)%MOD;
sum[o]=(sum[o]*mulv+addv*size[o])%MOD;
add[o]=(add[o]*mulv+addv)%MOD;
mul[o]=(mul[o]*mulv)%MOD;
}
void pushdown(int o) //下传add、mul、rev标记
{
if(rev[o]) //需要翻转o对应区间
{
rev[o]^=1;
rev[lson]^=1;
rev[rson]^=1;
swap(lson,rson);
}
if(mul[o]!=1||add[o]!=0) //本节点的mul和add标记没清
{
cal(lson,mul[o],add[o]);
cal(rson,mul[o],add[o]);
}
mul[o]=1;
add[o]=0;
}
void rotate(int x) //旋转x到其上一层去
{
int y=fa[x],z=fa[y],p,q;
if(ch[y][0]==x) p=0; else p=1;
q=p^1;
if(!isRoot(y))
{
if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
}
fa[x]=z;
fa[y]=x;
fa[ch[x][q]]=y;
ch[y][p]=ch[x][q];
ch[x][q]=y;
pushup(y);
pushup(x);
}
void splay(int x) //将x旋转到根节点上去
{
top=0;
q[++top]=x;
for(int i=x;!isRoot(i);i=fa[i])
q[++top]=fa[i];
for(int i=top;i;i--)
pushdown(q[i]);
while(!isRoot(x))
{
int y=fa[x],z=fa[y];
if(!isRoot(y))
{
if((ch[y][0]==x)==(ch[z][0]==y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x) //打通x到根节点的偏爱路径
{
int tmp=0;
while(x)
{
splay(x);
ch[x][1]=tmp;
pushup(x);
tmp=x;
x=fa[x];
}
}
void makeroot(int x) //使x成为根
{
access(x);
splay(x);
rev[x]^=1;
}
void link(int x,int y) //连接x、y,使得y成为x的父亲
{
makeroot(x);
fa[x]=y;
}
void cut(int x,int y) //断开x、y的连接,原来y是x的父亲
{
makeroot(x);
access(y);
splay(y);
ch[y][0]=fa[x]=0;
}
}lct;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i