[BZOJ4444] [Luogu 4155] [LOJ 2007] [SCOI2015]国旗计划(倍增)
[BZOJ4444] [Luogu 4155] [LOJ 2007] [SCOI2015]国旗计划(倍增)
题面
题面较长,略
分析
首先套路的断环为链。对于从l到r的环上区间,若l<=r,我们把它断成两个区间[l,r],[l+M,r+M][l,r],[l+M,r+M],否则断成[l,r+M],[l+M,r+M+M][l,r+M],[l+M,r+M+M](断成[l+M,2M][l+M,2M]也可以)
然后定义从区间[l,r]走到另一个与它相交的区间为1“步”。那么我们可以预处理出区间i走j步能够到达的区间右端点的最大值。注意到区间互不包含,先把区间按左端点为第一关键字,右端点为第二关键字排序。对于区间i,我们找到满足li<lj≤rili<lj≤ri的最大ljlj,那么rjrj就是走1步能到达的最大区间右端点。因为lj>lilj>li,所以rj>rirj>ri,否则区间j就会被i包含。由于排序过,j显然有单调性,双指针扫一遍就可以了。
sort(a+1,a+1+sz); int ptr=1; for(int i=1;i<=sz;i++){ while(ptr<sz&&a[ptr+1].l<=a[i].r) ptr++; if(ptr!=i) anc[i][0]=ptr; }
但是枚举走j步依然是O(n2)O(n2)的,可以用倍增优化。anc[i][j]anc[i][j]表示区间i走j步到达的右端点最大的区间编号。这个可以O(nlogn)O(nlogn)预处理。
查询的时候从i开始跳,一直跳到ranc[i][j]≥li+Mranc[i][j]≥li+M为止,需注意边界条件
int query(int x){ int ans=1; int r=a[x].l+len; //注意边界,比如3->5,5->1,1->3.必须要跳回原点3,所以是+len而不是+len-1 for(int i=log2n;i>=0;i--){ if(anc[x][i]!=0&&a[anc[x][i]].r<=r){//如果右端点<=i+M,就继续跳 ans+=(1<<i); x=anc[x][i]; } } if(anc[x][0]&&a[x].r<r){//上面求的是右端点<=i+M,可能跳到了<i+M的某一个位置,再跳一步就超过i+M,这种情况也是合法的。特判一下。 ans++; x=anc[x][0]; } return ans; //保证一定有解,所以不用判断a[x].r是否>=r }
代码
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define INF 0x3f3f3f3f #define maxn 2000000 #define maxlogn 25 using namespace std; int n,len; struct seg{ int l; int r; int id; seg(){ } seg(int _l,int _r,int _id){ l=_l; r=_r; id=_id; } friend bool operator < (seg p,seg q){ if(p.l==q.l) return p.r<q.r; else return p.l<q.l; } }a[maxn+5]; int sz; int log2n; int ans[maxn+5]; int anc[maxn+5][maxlogn+5]; int query(int x){ int ans=1; int r=a[x].l+len; //注意边界,比如3->5,5->1,1->3.必须要跳回原点3,所以是+len而不是+len-1 for(int i=log2n;i>=0;i--){ if(anc[x][i]!=0&&a[anc[x][i]].r<=r){ ans+=(1<<i); x=anc[x][i]; } } if(anc[x][0]&&a[x].r<r){ ans++; x=anc[x][0]; } return ans; } int main(){ int l,r; scanf("%d %d",&n,&len); log2n=log2(n*2); for(int i=1;i<=n;i++){ scanf("%d %d",&l,&r); if(l<=r){ a[++sz]=seg(l,r,i); a[++sz]=seg(l+len,r+len,i+n); }else{ a[++sz]=seg(l,r+len,i); a[++sz]=seg(l+len,r+len+len,i+n); } } sort(a+1,a+1+sz); int ptr=1; for(int i=1;i<=sz;i++){ while(ptr<sz&&a[ptr+1].l<=a[i].r) ptr++; if(ptr!=i) anc[i][0]=ptr; } for(int j=1;j<=log2n;j++){ for(int i=1;i<=sz;i++){ anc[i][j]=anc[anc[i][j-1]][j-1]; } } for(int i=1;i<=sz;i++){ if(a[i].id<=n) ans[a[i].id]=query(i);//注意要跳过(l+n,r+n),否则l+len会超过2*len导致答案错误 } for(int i=1;i<=n;i++) printf("%d ",ans[i]); }
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
· 一天 Star 破万的开源项目「GitHub 热点速览」
· 瞧瞧别人家的日期处理,那叫一个优雅!
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· 定时任务稳定性解决方案-healthchecks监控系统