1. 程式人生 > 其它 >NOI online 2022丹釣戰

NOI online 2022丹釣戰

看到這麼個東西賽時想用莫隊,但是打假了,所以就有了下面這個不三不四的程式碼

也就是很裸的暴力

void Push(int st,int ed){
    for(int i=st;i<=ed;++i){
        while(!q.empty()){
            Pair tmp=q.back();
            if(tmp.a == data[i].a || tmp.b<=data[i].b){
                q.pop_back();
            }
            else break;
        }
        q.push_back(data[i]);
        if(q.size()==1)ans++;
    }
}
     for(int i=1;i<=Q;++i){
         que[i].l=read(),que[i].r=read(),que[i].id=i;
     } 
     sort(que+1,que+Q+1,cmp);
     int l=0,r=0;
     for(int i=1;i<=Q;++i){
         if(l<que[i].l){
             q=deque<Pair>(),ans=0,l=que[i].l,r=que[i].r;
             Push(que[i].l,que[i].r); 
             que[i].ans=ans;           
             continue;
         }
         if(l==que[i].l){
             if(r<que[i].r){
                Push(r+1,que[i].r);
                r=que[i].r;
                que[i].ans=ans;
                continue;
             }
             if(r==que[i].r){
                que[i].ans=ans;
                continue;
             }
             if(r>que[i].r){
                q=deque<Pair>(),ans=0,r=que[i].r;
                Push(que[i].l,que[i].r); 
                que[i].ans=ans;
             }
         }    
     }
     sort(que+1,que+Q+1,cmp1);
     for(int i=1;i<=Q;++i){
         printf("%d\n",que[i].ans);
     }

對於每一個詢問,\(l\)肯定可以“成功”毋庸置疑。對於\(i\),如果滿足題目中的條件就入棧,否則\(i\)就是成功的。那些可以放進去的點對於答案沒有貢獻,暴力解法的瓶頸就在於遍歷了這些沒有用的點。

我們預處理出每一個點到的下一個"成功"點(初值為\(n+1\)),查詢時從\(l\)\(r\)一直跳就可以了。

for(int i=1;i<=n;++i){
    while(!s.empty() && !(a[i]!=a[s.top()] && b[i]<b[s.top()])){
          to[s.top()]=i;s.pop();
    }
    s.push(i);
}

for(int i=1;i<=q;++i){
    int ans=0;
    int l=read(),r=read();
    while(l<=r){
         ans++;l=to[l];
    }
    printf("%d\n",ans);
}

但是如果所有的點都是成功點,這種演算法會被卡到\(n^2\)。由於是不斷地往後跳可以用倍增優化

for(int i=1;i<=n;++i){
    while(!s.empty() && !(a[i]!=a[s.top()] && b[i]<b[s.top()])){
         to[s.top()][0]=i;s.pop();
    }
    s.push(i);
}
for(int i=1;i<=20;++i){
    for(int j=1;j<=n;++j){
        to[j][i]=to[to[j][i-1]][i-1];
    }
}
for(int i=1;i<=q;++i){
    int l=read(),r=read();
    for(int j=20;j>=0;--j){
        if(to[l][j] && to[l][j]<=r){
            l=to[l][j];ans+=(1<<j);
        }
    }
    printf("%d\n",ans);
}

民間資料倍增比不加倍增慢了將近一倍...