[CF1450D]Rating Compression

壹、题目描述 ¶

传送门 to CF.

贰、题解 ¶

如出一辙.

猜了一发 “答案除 \(k=1\) 时具有单调性” 然后就过了,不过是暴力的 \(\mathcal O(n\log n)\),二分答案 + \(\rm st\) 表优化最小值。

但是为什么使用 \(\rm st\) 表呢?因为俩 \(\log\) 被卡了 😦 .

但是为什么答案具有单调性呢?\(\sf closestool\) 给出了祂的证明:

假设当前区间长度为 \(k\) 并且合法,那么有这样一张图:

红蓝为两相邻区间,假设两个区间的最小值为 \(b_i,b_{i+1}\),由于他们的最小值是不同的,所以对于红蓝构成的整个大区间 \(U\),它的最小值不会出现在绿色的部分,更进一步,\(x,y\) 这两个位置,至少有一个位置是其所属区间的最小值,不失一般性地,假设 \(x\) 为其区间的最小值,那么有 \(a_x\) 比所有蓝色部分更小,那么对于红色区间最小值出现的地方,我们分情况讨论:

  • 当红最小值出现在绿色部分时,如果 \(k\leftarrow k+1\),那么此时整个区间的最小值为 \(b'_i=b_i=a_x=\min\{b_i,b_{i+1}\}\)
  • 当红最小值在 \(y\) 上,当 \(k\leftarrow k+1\) 时,蓝色区间会将 \(y\) 包住,这个时候 \(b'_i=\min\{a_x,a_y\}=\min\{b_i,b_{i+1}\}\)

综上,当 \(k\ge 2\) 时,如果有某个 \(\{b_i\}\) 是一个 \(1\sim n-k+1\) 的排列,那么当 \(k\) 增大时,\(b'=\{\min\{b_1,b_2\},\min\{b_2,b_3\},\min\{b_3,b_4\},...,\min\{b_{k-1},b_k\}\}\),显然,这变成了一个 \(1\sim n-k\) 的排列。

至于为什么 \(k=1\) 不行?因为绿色的部分消失了,两个区间没有绝对的关联了。

关于官方题解

对于 \(1<k<n\) 的情况,\(k\) 压缩应该包含恰好一次 \(1\),但 \(1\) 是最小的数,所以它应该出现在左端点或右端点,不失一般性地假设其位于 \(n\),那么 \(a[1:n-1]\)\(k\) 压缩应该是一个 \(2,3,4,...,n-1\) 的排列,而这是子问题,可以迭代求解。

那么,我们可以使用这样的算法求解:维护区间 \([l,r]\),初始 \(l=1,r=n\),然后迭代 \(i=1,2,3,...,n\),对于每个 \(i\),我们确保 \(a_l=i\;\or\;a_r=i\),并相应移动 \(l\)\(r\) 指针,然后我们检查 \(\min_{j=l}^r a_j=i+1\).

如果 \(i\) 是第一个失败的,那么对于所有 \(2\le k\le n-i+1\) 都是 \(0\),而以后都为 \(1\).

这实际上也从侧面说明的答案的单调性。

叁、参考代码 ¶

唔,\(\mathcal O(n\log n)\) 也算过了吧,那我贴这里了:

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;

// #define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxn=3e5;
const int logn=18;

bool yes[maxn+5];
int a[maxn+5], n;
int st[maxn+5][logn+5];
int lgg[maxn+5], pow2[logn+5];

inline void buildst(){
    rep(i, 1, n) st[i][0]=a[i];
    rep(j, 1, logn) rep(i, 1, n-pow2[j-1])
        st[i][j]=min(st[i][j-1], st[i+pow2[j-1]][j-1]);
}

inline int query(int l, int r){
    int tmp=lgg[r-l+1];
    return min(st[l][tmp], st[r-pow2[tmp]+1][tmp]);
}

inline bool check(int x){
    rep(i, 1, n) yes[i]=0;
    rep(i, x, n){
        int cur=query(i-x+1, i);
        // printf("query(%d, %d) == %d\n", i-x+1, x, query(i-x+1, x));
        if(yes[cur]) return false;
        yes[cur]=1;
    }
    rep(i, 1, n-x+1) if(!yes[i]) return false;
    return 1;
}

signed main(){
    lgg[0]=-1, pow2[0]=1;
    rep(i, 1, maxn) lgg[i]=lgg[i>>1]+1;
    rep(i, 1, logn) pow2[i]=pow2[i-1]<<1;
    rep(_, 1, readin(1)){
        n=readin(1);
        rep(i, 1, n) a[i]=readin(1);
        buildst();
        int l=2, r=n, mid, ans=n+1;
        while(l<=r){
            mid=(l+r)>>1;
            if(check(mid)) ans=mid, r=mid-1;
            else l=mid+1;
        }
        rep(i, 1, n) yes[i]=0;
        int spec=1;
        rep(i, 1, n) if(yes[a[i]]) spec=0;
            else yes[a[i]]=1;
        if(spec) putchar('1');
        else putchar('0');
        rep(i, 2, ans-1) putchar('0');
        rep(i, ans, n) putchar('1');
        Endl;
    }
    return 0;
}

肆、关键之处 ¶

还是递归处理子问题啊,为什么就是看不出来呢?或者说对于问题并没有分析相邻俩 \(k\) 都合法的关系?

posted @ 2021-07-16 21:55  Arextre  阅读(61)  评论(0编辑  收藏  举报