P8671 [蓝桥杯 2018 国 AC] 约瑟夫环 题解
约瑟夫环有 做法相信大家都知道。这里就不在介绍了,这里给出一个不知道这个结论的 简单做法。
考虑直接模拟题意,每次循环往后数 个然后把这个数给删掉,如果采用链表的话找 个是要超时的,考虑换个数据结构比如平衡树,支持 求排名,插入或删除。于是就打了个平衡树上去。
#include<ext/pb_ds/assoc_container.hpp> namespace pbds=__gnu_pbds; pbds::tree<int,pbds::null_type,std::less<>,pbds::rb_tree_tag,pbds::tree_order_statistics_node_update>s; int n,k; signed main(){ // freopen(".in","r",stdin); // freopen(".out","w",stdout); std::ios::sync_with_stdio(false);cin.tie(nullptr); cin>>n>>k; for(int i=1;i<=n;i++)s.insert(i); for(int i=1;--n;){ i=(i+k-2)%(n+1)+1; s.erase(s.find_by_order(i-1)); } cout<<*s.begin(); return 0; }
发现 pbds 的平衡树跑不过去,难道是太慢超时了?这该怎么办呢?众所周知基础平衡树的所有题都可以离线下来用树状数组去解决,那这里 find_by_order
这个操作就要用到树状数组上二分这个做法去实现。
代码:
constexpr int N=2e6+1; int n,k; struct{ int d[N]; void add(int x){for(;x<N;x+=x&-x)d[x]++;} void del(int x){for(;x<N;x+=x&-x)d[x]--;} int kth(int x){ int p=1<<std::__lg(N); for(int i=std::__lg(N)-1;~i;i--) if(d[p-(1<<i)]>=x)p-=(1<<i); else x-=d[p-(1<<i)]; return p; } }s; signed main(){ // freopen(".in","r",stdin); // freopen(".out","w",stdout); std::ios::sync_with_stdio(false);cin.tie(nullptr); cin>>n>>k; for(int i=1;i<=n;i++)s.add(i); for(int i=1;--n;){ i=(i+k-2)%(n+1)+1; s.del(s.kth(i)); } cout<<s.kth(1); return 0; }
这里简单介绍一下树状数组上二分这个算法:先把当前位置调整成整个数组总和的位置(也就是 1..0000
,这样才能保证 都被 包含,为了方便,空间开两倍),从高位向低位考虑,如果左儿子比 大表示答案一定在左儿子,进入左儿子就行了,否则就在右儿子里面,把排名 减去左儿子的总和就行了。
CF1354D 这题也是采取树状数组去代替平衡树,也要用到树状数组上二分这个办法,可以去做一做。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
2020-11-02 卡常技巧