[BZOJ4444] [Luogu 4155] [LOJ 2007] [SCOI2015]旗プログラム(乗算)
フェイス質問
問題は少し、長い顔
分析
ルーチン最初のリングが壊れたチェーンです。LからRリングの範囲のために、場合L <= R、我々は、2つの部分にそれを破る\([L、R]、[M + L、M Rを+] \) 、またはに分割\([ Lは、R + M]、[M + Lは、R + M + M] \) (に分割\([L + M、2M ] \) であってもよいです)
その後、区間[L、R]他に移動を定義し、それは間隔1交差する「ステップ」を その後、我々は到達することができるiが最大間隔Jステップの右端を外出間隔をの前処理することができます。相互に排他的な間隔、最初のセクション最初のキーの左端、右ソートの第2のキー点を指摘しました。インターバル私のために、私たちは満足見つける\(L_iを<l_j \当量R_iと \) の最大の\(l_jを\) 、そう\(r_j \) 1つのステップの最大範囲は右のポイントに到達することができます取ることです。以来\(l_j> L_iを\) 、そう\(r_j> R_iと\)、そうでない場合は、私が含まれることになる区間jを。それ以上のソートので、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(N ^ 2) \) 、二重に最適化することができます。\(ANC [I] [J] \)私は間隔の最大数に到達するために、右のステップに行く間隔点jを表します。これは、することができ、\(O(N \ログN )\) の前処理。
クエリの最初からジャンプするとき、私は、ジャンプ有する\(R_ {ANC [I] [J]} \ GEQ L_iを+ 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]);
}