[Jsoi2016]位运算

题面

<center> 5758: [Jsoi2016]位运算<center>
<center>时间限制:20秒 内存限制:512MB <center>

题目描述
  JYY最近在研究位运算。他发现位运算中最有趣的就是异或(xor)运算。对于两个数的异或运算,JYY发现了一个结论:两个数的异或值为0当且仅当他们相等。于是JYY又开始思考,对于N个数的异或值会有什么性质呢?
【问题描述】
  JYY想知道,如果在0到R-1的范围内,选出N个不同的整数,并使得这N个整数的异或值为0,那么一共有多少种选择的方法呢?(选择的不同次序并不作重复统计,请参见样例)JYY是一个计算机科学家,所以他脑海里的R非常非常大。为了能够方便的表达,如果我们将R写成一个0-1串,那么R是由一个较短的0-1串S重复K次得到的。比如,若S=“101”,K=2,那么R的2进制表示则为“101101”。由于计算的结果会非常大,JYY只需要你告诉他选择的总数对10^9+7取模的结果即可。
输入
第一行包含两个正整数N和K。
接下来一行包含一个由0和1组成的字符串S
我们保证S的第一个字符一定为1。
3<=N<=7,1<=k<=10^5,1<=|S|<=50
输出
一行一个整数,表示选择的方案数对10^9+7取模的值。
样例输入

3 1
100

样例输出

1

提示
对于第一个样例, 唯一的一种选择方法是选择 {1, 2, 3}。

题解

  我推出了规律:所有N个数的第i位中‘1’的个数必须为偶数,才能使最终结果为0,但是接下来就没有思路了……
  在网上搜了题解:题解链接,但是题解过于简单,代码也没啥注释,也许是wtcl并没有看明白,又研究了好久才搞懂,并把代码加上了注释。
  大体思路是这样的:首先,定义了一个结构体用来定义矩阵,其中包括矩阵的初始化(类似于Java的构造方法);然后定义了两个函数(方法):矩阵的乘法和矩阵幂;以上都是辅助性质的。以下为主要内容:为实现N个数不相同,规定A~1~<A~2~<……<A~N~,用trans函数(方法)实现,然后进行状态压缩用lim的二进制值来表示N个数每一位的取值(0 or 1),最后求出一个|s|的结果,再运用矩阵快速幂求出k个|s|的结果。

源码

#include <iostream>
#include <cstring>
using namespace std;
const int mod=1e9+7;
int n,k,len,lim,f[55][130];
char s[55];
typedef unsigned long long ull;
struct matrix {//矩阵结构体定义 
    ull a[130][130];
    matrix(int t=0) {//初始化一个对角线为t,其他位置为0的矩阵 
        memset(a,0,sizeof(a));
        for(int i=0;i<=lim;i++) a[i][i]=t;
    } 
}A;
matrix fun(matrix a,matrix b) {//矩阵乘法 
        matrix c(0);
        for(int i=0;i<=lim;i++)
            for(int k=0;k<=i;k++) if(a.a[i][k])
                for(int j=0;j<=k;j++)
                    ((c.a[i][j]+=(ull)a.a[i][k]*b.a[k][j]) >= mod) ? c.a[i][j]%=mod : 0;
        for(int i=0;i<=lim;i++)
            for(int j=0;j<=i;j++)
                (c.a[i][j]>=mod) && (c.a[i][j]%=mod);
        return c;
    }
matrix power(matrix a,int b){//矩阵快速幂 
    matrix rs(1);
    for(;b;b>>=1, a=fun(a,a)) if(b&1) rs=fun(rs,a);
    return rs;
}
int trans(int pre,int now,int pos) {//判断每一位的N个数是否按递增排序 
    int c=0;
    for(int j=n-1;j;j--)
        if((pre>>j)&1) {
            int a=(now>>j)&1, b=(now>>(j-1))&1;
            if(a>b) return -1;//此时~-1=0 
            if(a==b) c|=(1<<j);
        }   
    if(pre&1) {
        int tl=0;
        while(tl<n && (pre>>tl)&1) 
            if((now>>tl)&1 && s[pos]=='0') return -1;//此时~-1=0
            else ++tl;
        if((now&1)==s[pos]-'0') c|=1;
    }
    return c;
} 
int main(int argc, char** argv) {
    cin>>n>>k>>(s+1);
    len=strlen(s+1);
    lim=(1<<n)-1;//状态压缩:用来表示n个数每一位的取值 
    for(int i=0;i<=lim;i++) {
        memset(f,0,sizeof(f));
        f[0][i]=1;
        for(int j=1;j<=len;++j)
            for(int pre=0;pre<=lim;++pre) //之前的状态 
                for(int now=0;now<=lim;++now) { //现在要放的状态 
                    if(__builtin_popcount(now)&1) continue;
// __builtin_popcount获取now的二进制数中1的个数,只有1的个数为偶数时才可能取异或为0 
                    int t=trans(pre,now,j);//判断N个数的第j位是否递增 
                    if(~t) f[j][t]=(f[j][t]+f[j-1][pre])%mod;//t=-1时,~t=0,不进行 
                }      
        for(int j=0;j<=lim;j++)//获取最终结果 
            A.a[i][j]=f[len][j]; 
    }
    matrix B=power(A,k);//进行矩阵快速幂 
    int ans=B.a[lim][0]%mod;//得出最后结果 
    cout<<ans;
    return 0; 
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值