1 条题解

  • 0
    @ 2025-10-17 18:36:41

    题解

    我们要按字典序枚举所有长度为 nn、相邻异字符次数不超过 kk 的串,并认为一个串与其反转后相同(180° 旋转)是同一块石头,取两者中字典序较小的一种作为正规表示。为了直接在正规表示序列上做排名,代码使用“逐位构造 + 动态规划”:

    1. p[i] 表示第 ii 位当前被强制为 I/0X/1,初始全为 -1。主程序按字典序从前往后尝试把第 ii 位设为 I,调用 cal() 统计这种前缀下的合法补全个数;若不足以覆盖目标排名,就改设为 X 并相应地扣掉数量。
    2. cal() 把串从两端往中间成对地扩展。状态 dp[pos][cnt][L][R][flag] 含义为:已经确定了前 pos 对字符(如果 nn 为奇数,中间字符单独处理),累计有 cntcnt 次相邻异字符,最外层字符分别为 LLRRflag 表示之前是否已经确定“左边严格小于右边”(保证我们只计入正规表示)。每次选择新的一对字符 (a,b)(a,b) 时:
      • 如果这一对是中心处且 aba \ne b,则需要额外计一次变化(因为串与反串不同)。
      • 在没有证明左串严格小于右串之前(flag=0),必须保持 bab \ge a,否则翻转后的串会更小。
      • 邻位之间是否不同通过 apreva \oplus prev 累加到 cntcnt 中。
    3. cntkcnt \le k 的终态都会累积到答案里;如果最外层字符已经固定,还要在 DP 开始前检查它们是否满足 p[n]p[1]p[n] \ge p[1](否则正规表示应该是翻转后的那一侧)。

    整套 DP 只依赖于 nn 的一半,因此复杂度约为 O(n2)O(n^2),配合外层的逐位扫描即可在 n60n \le 60 的范围内完成排名查询;若最终计数仍小于所需排名,就说明不存在对应的石头,输出 NO SUCH STONE

    //test
    #include<iostream>
    #include<cstring>
    #define mem(a,b) memset(a, b, sizeof(a))
    #define rep(i,a,b) for(int i = (a); i <= (b); i++)
    #define per(i,b,a) for(int i = (b); i >= (a); i--)
    #define N 66
    #define ll long long
    using namespace std;
    
    ll dp[N][N][2][2][2], rnk;
    int p[N], n, k;
    
    ll cal(){
        mem(dp, 0);
        if(~p[n]){
            if(p[n] < p[1]) return 0;
            dp[1][0][p[1]][p[n]][p[n]>p[1]] = 1;
        }
        else rep(i,p[1],1) dp[1][0][p[1]][i][i > p[1]] = 1;
    
        rep(i,1,(n+1)/2-1) rep(j,0,n-1){
            int sa = (~p[i+1] ? p[i+1] : 0), sb = (~p[n-i] ? p[n-i] : 0);
            rep(a,sa, (~p[i+1]?sa:1)) rep(b,sb, (~p[n-i]?sb:1)){
                if((n&1) && i == (n+1)/2-1 && a != b) continue;
                int ex = ((i == (n+1)/2-1) && (a^b));
                rep(c,0,1) rep(d,0,1) rep(e,0,1) if(dp[i][j][c][d][e])
                    if(b >= a || e) dp[i+1][j+(a^c)+(b^d)+ex][a][b][e|(b>a)] += dp[i][j][c][d][e];
            }
        }
        ll ans = 0;
        rep(a,0,1) rep(b,0,1) rep(c,0,1) rep(j,0,k)
            ans += dp[(n+1)/2][j][a][b][c];
        return ans;
    }
    
    int main(){
        cin>>n>>k>>rnk;
        mem(p, -1);
        rep(i,1,n){
            p[i] = 0;
            ll I = cal();
            if(rnk > I){
                rnk -= I, p[i] = 1;
                ll X = cal();
                if(X < rnk) return puts("NO SUCH STONE"), 0;
            }
        }
        rep(i,1,n) cout<< (p[i] ? 'X' : 'I');
        cout<<endl;
        return 0;
    }
    
    • 1

    信息

    ID
    3244
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    1
    已通过
    1
    上传者