zoukankan      html  css  js  c++  java
  • Gym102956-C. Brave Seekers of Unicorns

    题目链接
    思路
    (dp[i]:)表示以(i)为结尾的所有方案数。
    假设前一位数是(j),再前一位数是(k),连续的三个数满足条件(k<j<i),那么当(k=i igoplus j)时便是不合法的方案,需要减去。所以可以列出这样的(dp)方程:(dp[i]=sum_{j=1}^{i-1}(dp[j]-dp[j igoplus i]))
    对上述式子的括号拆除即是
    (dp[i]=sum_{j=1}^{i-1}dp[j]-sum_{k<kigoplus 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 igoplus j igoplus k=0)(1leq 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
    ", sum[n]);
    }
    
    int main() {
    //     freopen("in.txt", "r", stdin);
        solve();
        return 0;
    }
    
    
  • 相关阅读:
    golang sql连接池的实现解析
    golang使用rabbitmq正确姿势
    golang使用rabbitmq多个消费者
    golang网关之手动实现反向代理
    golang exec.Command执行脚本 杀死子进程
    exec: "gcc": executable file not found in %PATH%
    golang操作mongodb
    grpc之 普通流 、服务端流、 客户端流 、双向流模式
    grpc-POST提交主订单数据(gateway实现http api)
    grpc之protobuf常用语法速学
  • 原文地址:https://www.cnblogs.com/ZX-GO/p/14497485.html
Copyright © 2011-2022 走看看