Educational Codeforces Round 190 D. Good Schedule

Good Schedule


原题链接

解题思路

核心观察:
1.区间计数问题,如果存在单调性,可以考虑固定左端点查询最大右端点或固定右端点查询最小左端点,这题显然可以固定左端点数合法右端点数量
2.对于每个区间从区间内 $ {a_i=1} $ 的第一个位置开始,每个位置贪心跳到后一个比当前位置大 \(1\) 的位置。因此可以记录一下后缀数组快速找到比当前大 \(1\) 的最近位置,以及区间内第一个 \(1\) 出现的位置
3.以上做法还是可以卡到 \(n^2\) ,考虑继续优化,发现从区间 端点 \(l\) 跳到的最远位置是确定的,每个点能够跳到最远的位置只进行一次计算后就能反复调用。这可以通过倒序遍历预处理技巧或记忆化搜索技巧做到,算法也可以达到严格 $ O(n) $的复杂度。

AC code

//Stop learning useless algorithms, go and solve some problems, learn how to use binary search.
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const ll INF=1e17;
void solve(){
    int n;cin>>n;
    vector<ll>a(n+1);
    vector<ll>b(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    vector<int>lasA(n+1,n+1),lasB(n+1,n+1);
    vector<int>las(n+2,n+1);
    vector<int>nxt(n+1,n+1);
    for(int i=n;i>=1;i--){
        lasA[a[i]]=i;
        lasB[b[i]]=i;
        if(a[i]+1<=n) nxt[i]=min(lasA[a[i]+1],lasB[a[i]+1]);
    }
    ll ans=0;
    vector<int>dp(n+2,-1);
    auto dfs=[&](auto &&dfs,int r) ->int {
        if(dp[r]!=-1) {
            return dp[r];
        }
        if(r>n||a[r]!=b[r]){
            return dp[r]=r;
        }
        return dp[r]=dfs(dfs,nxt[r]);
    };
    for(int i=n;i>=1;i--){       
        las[i]=las[i+1];
        if(a[i]==1||b[i]==1) las[i]=i;
        bool f=true;
        int r=i;
        while(true){
            if(f) {
                r=las[r];
                f=false;
            }
            else {
                r=dfs(dfs,r);
                break;
            }
            if(r>n||a[r]!=b[r]) break;
        }
        ans+=r-i;
    }
    cout<<ans<<endl;
}
int main(){
    cin.tie(0)->ios::sync_with_stdio(false);
    int T=1;cin>>T;
    while(T--) solve();
    return 0;
}
posted @ 2026-05-19 14:34  usedchang  阅读(74)  评论(0)    收藏  举报