约瑟夫环小结(线段树)
Posted despair_ghost
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了约瑟夫环小结(线段树)相关的知识,希望对你有一定的参考价值。
题目大意:
输入初始人数编号为1~n,以及初始密码m,输出出局序列.
解法一:用线段树可解.
Segtree节点存储左右区间和该区间下包含的人数.
void Build(int p,int left,int right)表示从编号为p,区间为[l,r]的节点开始向下建树.
int Update(int p,int id)表示从编号为p的节点开始查询在新队伍中编号为id的人出局,返回该人在最初队伍中的编号.
而temp=(temp+m-1)%seg[1].manum;语句用于求出下一个出局的人在新队伍中的编号id,即传入Update()函数参数中的id.
Update()函数在递归过程中实现了给新队伍人员从1~n重新编号,如果id<=seg[p].manum,即p节点左子树最多能从1编号到seg[p].manum.
说明新队伍中编号为id的人在p节点的左子树中,则继续递归;反之,则在p节点的右子树中.
#include<cstdio> #define maxn 200010 struct Segtree { int l,r; int manum; }seg[4*maxn]; void Build(int p,int left,int right) { seg[p].l=left; seg[p].r=right; if(left==right){ seg[p].manum=1; return; } int mid=(left+right)/2; Build(2*p,left,mid); Build(2*p+1,mid+1,right); seg[p].manum=seg[2*p].manum+seg[2*p+1].manum; } int Update(int p,int id) { seg[p].manum--; if(seg[p].l==seg[p].r) return seg[p].l; if(id<=seg[2*p].manum) Update(2*p,id); else Update(2*p+1,id-seg[2*p].manum); } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF){ printf("%d",m); Build(1,1,n); Update(1,m); int temp=m; while(n>=2){ temp=(temp+m-1)%seg[1].manum; if(temp==0) temp=seg[1].manum; int id=Update(1,temp); printf(" %d",id),n--; } printf("\n"); } }
以上是关于约瑟夫环小结(线段树)的主要内容,如果未能解决你的问题,请参考以下文章