Gym102956-C. Brave Seekers of Unicorns

题目链接
思路
\(dp[i]:\)表示以\(i\)为结尾的所有方案数。
假设前一位数是\(j\),再前一位数是\(k\),连续的三个数满足条件\(k<j<i\),那么当\(k=i \bigoplus j\)时便是不合法的方案,需要减去。所以可以列出这样的\(dp\)方程:\(dp[i]=\sum_{j=1}^{i-1}(dp[j]-dp[j \bigoplus i])\)
对上述式子的括号拆除即是
\(dp[i]=\sum_{j=1}^{i-1}dp[j]-\sum_{k<k\bigoplus i<i}dp[k]\)
当计算\(dp[i]\)时,\(\sum_{j=1}^{i-1}dp[j]\)中的每个\(dp[j]\)其实都已经算完,那么可以用一个\(sum[i-1]\)数组记录\(\sum_{j=1}^{i-1}dp[j]\)
接下来需要考虑的是如果已经确定了\(i\),如何处理\(j,k\)的关系。\(j\)的所有情况我们已经用\(sum[i-1]\)计算掉了,所以需要考虑这个式子的后半部分和这个\(k\)到底如何去取。
假定对于一个\(i\)的二进制表示为\(1101010\)。因为考虑的是\(i \bigoplus j \bigoplus k=0\)\(1\leq k<j<i\)的情况,所以对于\(i,j,k\)相同的位,最多同时出现两个\(1\),最高位已经在\(i\)中出现了一个1,要保证\(j>k\),所以要保证\(j\)的最高位也为1,而\(k\)的最高位为0。当确定了\(j\)的最高位,\(j\)后面的位数上的01就可以在满足\(j<i\)的情况下随意表示了。然后考虑\(k\),因为\(k \geq 1\),所以\(k\)至少有一位为\(1\)。接下来就考虑\(k\)后面几位的01表示,如果\(k\)确定,那么也可唯一确定\(j\)。暴力找\(k\)的复杂度到达了O(n),可以根据二进制的性质,我们枚举\(i\)在二进制表示下除最高位的每一位,若这一位为\(1\),那么就令这一位是\(k\)的最高位。假设这一位是第\(d\)位,\(k\)为最高位所有可表示的数字范围即是\((1<<d) \leq k <(1<<(d+1)-1))\),因为\(k\)的前几位都是0,所以对应的\(j\)的那几位也都是0,且第\(d\)位上已经有两个1了,所以\(j\)除最高位以外的1的位置一定小于第\(d\)位,那么此时无论后面几位的情况如何,都能满足\(j<i\)
例如这个例子:(x表示可以随意取)
\(i:101000\)
\(j:1xxxxx\)
\(k:0xxxxx\)
在这种情况下,当\(k\)的第三位当成最高位且取1时,\(k:001xxx\)\(j:100xxx\),那么就能确定\(j\)的前几位,那么\(j\)后面几位不管怎么取一定满足\(j<i\)
\(k\)的最高位不是在第三位取\(1\),假设是在第二维上。即\(k:0001xx\),那么对于第三位上要满足条件,\(j\)就变成了\(j:1011xx\),这种情况下可以显而易见的发现\(j>i\)不满足条件了。
所以对于考虑后面枚举所有\(k\)的情况就变成了当确定了\(k\)的最高位\(d\),那么区间上所有的表示就变成了\(sum[(1<<(d+1)-1)]-sum[(1<<d)-1]\)。具体可以看代码。
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 1e6 + 10;
const int mod = 998244353;
LL dp[N], sum[N];

void solve() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        dp[i] = sum[i - 1] + 1;	// +1表示只有一个数字,且这个数字就是i的情况。
        int x = 1;
        while(x << 1 < i) {
            if(x & i) {
                dp[i] = (dp[i] - (sum[(x << 1) - 1] - sum[x - 1]) + mod) % mod;
            }
            x <<= 1;
        }
        sum[i] = (sum[i - 1] + dp[i]) % mod;
    }
    printf("%lld\n", sum[n]);
}

int main() {
//     freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

posted @ 2021-03-07 23:52  这知识他不进我的脑子  阅读(75)  评论(0编辑  收藏  举报