Atcoder356

Atcoder356

C

解题思路

  • 先从数据量上看,\(2^{15}\)=32768,那么时间复杂度为O(\(2^{n}*m*n\))大约在\(4*10^{7}\)左右,可以直接跑暴力枚举。
  • 按照正常思路,我们需要创建一个二维数组来存储测试中的数据。然后再创建一个一维数组来模拟n把钥匙的选和不选。这样暴力枚举上的对照比较麻烦。依照题意,选和不选可以用1和0表示。那么n把钥匙的所有情况就是一个二进制数从零开始到n-1的遍历。既然枚举的数可以用数字表示,那么测试数据的数也可以用同样的方式进行压缩成一个数,数组就可以压缩成一维来存储测试中的数据。
  • 那么该如何对照测试和枚举的数呢?首先,我们先明确正确的钥匙个数是数据的二进制中1的个数(这里可以用__builtin_popcount()实现),那么按照题意,我们要验证枚举数在测试中的结果,即按照枚举的情况在测试中的开门数是否大于k,通过举例,假如在二进制中某一位的门要是开的,那么需要测试和枚举的数据在二进制的同一位中要同时为1,那么就可以用位运算(&)来验证开的门的个数。
#include <bits/stdc++.h>
#define debug1(X) std::cout << #X << ": " << X << '\n'
#define debug2(X) std::cout << #X << ": " << X << ' '
using i64 = long long;
void solve()
{      
    int n,m,k;
    std::cin>>n>>m>>k;
    std::vector<int>a(m);
    std::vector<char>ok(m);   
    int ans=0;
    for(int i=0;i<m;i++){
        int c;
        std::cin>>c;
       for(int j=0;j<c;j++){
            int x;
            std::cin>>x;
            x--;
            a[i]+=(1<<x);
       }
       std::cin>>ok[i]; 
    }    
    for(int i=0;i<(1<<(n));i++){
        bool jub=true;
       for(int j=0;j<m;j++){
            int cnt=__builtin_popcount(i&a[j]);
            if(cnt>=k&&ok[j]=='x'){
                jub=false;
                break;
            }
            if(cnt<k&&ok[j]=='o'){
                jub=false;
                break;
            }
       }
       if(jub){
        ans++;
       }
    }
    std::cout<<ans<<"\n";    
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int t = 1;
    // std::cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

D

题意大致就是求m在二进制中每一位上1在1~n中出现的次数之和。
那如何求某一位上1在1~n之前出现过的次数呢?
我们知道某一位(n)1出现从1~\(2^n\)次数为\(2^{n-1}\).

  • 例如100这个二进制数从右到左第三位出现的次数从001到100为\(2^2\).
    那么我们就可以因此将某一个二进制数拆分为两个部分。

举个例子,求100100的从右到左第三位为1在1~100100出现的次数,我们就将100100分为左边100,右边100.
那么我们可以简单知道右边100出现的次数应该是\(2^2\),而左边则是[0,100)共8个数,就是100可以看成0100,那么从0100到100100中间有0到100(即8个数)的右边100出现次数。所以总的次数就是左边的数。

那如果100100变为100111呢?
按照上述条件再来判断就会发现,我们只判断到了1100100的情况,那么再100100100111的情况就要另作边界判断,也很简单,就是右边111~100的个数再加上就行。

#include <bits/stdc++.h>
#define debug1(X) std::cout << #X << ": " << X << '\n'
#define debug2(X) std::cout << #X << ": " << X << ' '
using LL = long long;
void solve()
{      
    LL ans=0;
    LL mod=998244353;
    LL n,m;
    std::cin>>n>>m;
    for(int i=0;i<60;i++){
        if((m>>i)&1){
            LL w=1LL<<i;
            ans+=n/(w*2)*w+std::max(0LL,n%(2*w)-w+1);
        }
        ans%=mod;
    } 
    std::cout<<ans<<"\n"; 
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int t = 1;
    // std::cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
posted @ 2024-06-03 20:35  拍手称快  阅读(7)  评论(0编辑  收藏  举报