HNUCM2020年春季ACM集训队选拔赛(4)题解

问题 A: 小白鼠

题目描述

N只小白鼠(1 <= N <= 100),每只鼠头上戴着一顶有颜色的帽子。
现在称出每只白鼠的重量,要求按照白鼠重量从大到小的顺序输出它们头上帽子的颜色。
帽子的颜色用“red”,“blue”等字符串来表示。
不同的小白鼠可以戴相同颜色的帽子。白鼠的重量用整数表示。

输入

多案例输入,每个案例的输入第一行为一个整数N,表示小白鼠的数目。
下面有N行,每行是一只白鼠的信息。第一个为不大于100的正整数,表示白鼠的重量;
第二个为字符串,表示白鼠的帽子颜色,字符串长度不超过10个字符。

注意:白鼠的重量各不相同。

输出

每个案例按照白鼠的重量从大到小的顺序输出白鼠的帽子颜色。

样例输入

3
30 red
50 blue
40 green

样例输出

blue
green
red

思路

用结构体储存每一个白鼠的重量和帽子颜色,sort排序

#include<bits/stdc++.h>
using namespace std;
struct node{
    int weight;
    char color[15];
    bool operator<(const node x){
        return weight>x.weight;
    }
}w[1005];
int main(){
    int n;
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;++i){
            scanf("%d %s",&w[i].weight,w[i].color);
        }
        sort(w+1,w+n+1);
        for(int i=1;i<=n;++i){
            printf("%s\n",w[i].color);
        }
    }
    return 0;
}

问题 B: 放苹果

题目描述

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
(用K表示)5,1,1和1,5,1 是同一种分法。

输入

每行均包含二个整数M和N,以空格分开。1<=M,N<=10。

输出

对输入的每组数据M和N,用一行输出相应的K。

样例输入

7 3

样例输出

8

思路

比较容易想到的
递归每次放置的苹果数升序或者降序,便可以去除重复的分法
如每次递归传三个参数now(当前剩下的苹果数),n(当前剩下的盘中数),x(当前盘中最少放置的苹果数)
当now=0时返回1,也就是求出了一种分法
如果now!=0但是n=0说明此分法无意义,返回0

#include<bits/stdc++.h>
using namespace std;
int dfs(int now,int n,int x){
    if(now==0)
        return 1;
    if(n==0)
        return 0;
    int ans=0;
    for(int i=x;i<=now;++i){
        ans+=dfs(now-i,n-1,i);
    }
    return ans;
}
int main(){
    int n,m;
    while(~scanf("%d %d",&m,&n)){
        printf("%d\n",dfs(m,n,1));
    }
    return 0;
}

另一种思路是考虑苹果和盘子的关系
递归函数f(m,n)表示m个苹果放n个盘中里

  • 当m<n
    苹果不够,注定有n-m个盘子没有苹果,也就相当于在m个盘子里放m个苹果
    即f(m,n)=f(m,m);

  • 当m>=n
    苹果充足,可以分两种情况考虑

    • 每个盘子至少一个苹果
      对于这种情况可以先在每个盘中放一个苹果再分也就是f(m,n)=f(m-n,n);
    • 至少有一个盘子中没有苹果
      对于这种情况可以先将一个盘子拿出来再分也就是f(m,n)=f(m,n-1);

    即f(m,n)=f(m-n,n)+f(m,n-1);

    PS:这种写法在hnucm oj过不了(内存超限…),但是poj(题目原主人)能过…有点迷
#include <bits/stdc++.h>
using namespace std;
int dfs(int m,int n){
    if(m==0||n==1)
        return 1;
    if(m<n)
        return dfs(m,m);
    else
        return dfs(m,n-1)+dfs(m-n,n);
}
int main(){
    int n,m,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&m,&n);
        printf("%d\n",dfs(m,n));
    }
    return 0;
}



问题 C: 游船出租

题目描述

现有公园游船租赁处请你编写一个租船管理系统。
当游客租船时,管理员输入船号并按下S键,系统开始计时;
当游客还船时,管理员输入船号并按下E键,系统结束计时。
船号为不超过100的正整数。当管理员将0作为船号输入时,
表示一天租船工作结束,系统应输出当天的游客租船次数和平均租船时间。
注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有租船没有还船,
或者只有还船没有租船的纪录,系统应能自动忽略这种无效纪录。

输入

测试输入包含若干测试用例,每个测试用例为一整天的租船纪录,格式为:
船号(1~100) 键值(S或E) 发生时间(小时:分钟)
每一天的记录保证按时间递增的顺序给出。当读到船号为-1时,全部输入结束,相应的结果不要输出。

输出

对每个测试用例输出1行,即当天的游客租船次数和平均租船时间(以分钟为单位的精确到个位的整数时间)。

样例输入

1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00
-1

样例输出

2 196
0 0
1 60

思路

用一个数组存储船的被租时间(如vis[i]表示编号为i的船的被租时间)
当输入的信息是租船信息

  • 如果vis[i]已经有值则表示船已经被租走,此信息无效
  • 如果没有值便将时间存入vis[i]中

当输入的信息是还船信息

  • 如果vis[i]没有值表示船未被借走,此信息无效
  • 如果有值便更新借还次数和借船时间再将vis[i]置0

//每一天的租还情况是独立的,所以每次输出后初始化

#include<bits/stdc++.h>
using namespace std;
int vis[105];//船被借出的时间
int main()
{
    int a,b,c,num=0,cnt=0;
    char x;
    while(~scanf("%d",&a)){
        if(a==-1)
            break;
        getchar();//获取空格
        scanf("%c %d:%d",&x,&b,&c);
        if(a==0){
            if(num==0){
                printf("0 0\n");
            }else{
                printf("%d %d\n",num,(cnt+num-1)/num);//向上取整
            }
            num=0,cnt=0;//清空数据
            memset(vis,0,sizeof(vis));
            continue;
        }
        if(x=='S'){//租船
            if(vis[a]==0){
                vis[a]=b*60+c;
            }
        }else{//还船
            if(vis[a]){
                ++num;
                cnt+=b*60+c-vis[a];
                vis[a]=0;
            }
        }
    }
    return 0;
}

问题 D: 数字交换

题目描述

输入一个数n,然后输入n个数值各不相同,调换数组中最大和最小的两个数,然后输出。

输入

测试数据有多组,输入n(1<=n<=20),接着输入n个数。

输出

对于每组输入,输出交换后的结果。

样例输入

2
1 3

样例输出

3 1

思路

循环找到最小值和最大值的下标…交换…输出

#include<bits/stdc++.h>
using namespace std;
int s[105];
int main()
{
    int n,a,aa,b,bb;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;++i){
            scanf("%d",&s[i]);
        }
        a=b=s[1],aa=bb=1;
        for(int i=2;i<=n;++i){
            if(s[i]>a){
                a=s[i];
                aa=i;
            }else if(s[i]<b){
                b=s[i];
                bb=i;
            }
        }
        swap(s[aa],s[bb]);
        for(int i=1;i<n;++i){
            printf("%d ",s[i]);
        }
        printf("%d\n",s[n]);
    }
    return 0;
}

问题 E: 搬水果

题目描述

在一个果园里,小明已经将所有的水果打了下来,并按水果的不同种类分成了若干堆,小明决定把所有的水果合成一堆。
每一次合并,小明可以把两堆水果合并到一起,消耗的体力等于两堆水果的重量之和。
当然经过 n‐1 次合并之后,就变成一堆了。小明在合并水果时总共消耗的体力等于每次合并所耗体力之和。
假定每个水果重量都为 1,并且已知水果的种类数和每种水果的数目。
你的任务是设计出合并的次序方案,使小明耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种水果,数目依次为 1,2,9。可以先将 1,2 堆合并,新堆数目为3,耗费体力为 3。
然后将新堆与原先的第三堆合并得到新的堆,耗费体力为 12。
所以小明总共耗费体力=3+12=15,可以证明 15 为最小的体力耗费值。

输入

每组数据输入包括两行,第一行是一个整数 n(1<=n<=10000),表示水果的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数(1<=ai<=1000)是第 i 种水果的数目。

输出

对于每组输入,输出一个整数并换行,这个值也就是最小的体力耗费值。
输入数据保证这个值小于 2^31。

样例输入

3
9 1 2
0

样例输出

15

思路

贪心思想,每次将最小的两堆果子合并,直到最后只剩一堆果子
正确性证明
果子的合并可以看成树节点的合并如图

在这里插入图片描述最后会得到一棵二叉树,而耗费的体力值就等于每一个叶子节点到根节点的距离×该节点果子重量(这是显然的)
要最小化体力值也就是让更重的果子离根节点越近(权重思想)(哈夫曼树思想)
所以贪心策略可行

如果每一次合并都排序一次肯定会超时
所以用优先队列处理
优先队列详解

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,x;
    while(~scanf("%d",&n)&&n){
        priority_queue<int,vector<int>,greater<int> >pq;
        for(int i=1;i<=n;++i){
            scanf("%d",&x);
            pq.push(x);
        }
        int ans=0;
        while(pq.size()>1){
            int a=pq.top();
            pq.pop();
            int b=pq.top();
            pq.pop();;
            ans+=(a+b);
            pq.push(a+b);
        }
        printf("%d\n",ans);
    }
    return 0;
}

其实还有更优的解法…时间复杂度为O(n)╰(●’◡’●)╮
不过这个题目数据不是很大,用优先队列就行


问题 F: 打牌

题目描述

牌只有1到9,手里拿着已经排好序的牌a,对方出牌b,用程序判断手中牌是否能够压过对方出牌。
规则:出牌牌型有5种。
[1] 一张 如4 则5…9可压过
[2] 两张 如44 则55,66,77,…,99可压过
[3] 三张 如444 规则如[2]
[4] 四张 如4444 规则如[2]
[5] 五张 牌型只有12345 23456 34567 45678 56789五个,后面的比前面的均大。

输入

输入有多组数据。
每组输入两个字符串(字符串大小不超过100)a,b。
a字符串代表手中牌,b字符串代表出的牌。

输出

压过输出YES 否则NO。

样例输入

12233445566677
33

样例输出

YES

思路

模拟题
用数组保存字符串a中每一张牌出现的次数
按字符串b的长度分情况讨论

  • b的长度为为1到4,看a中牌x+1到9中有没有一类牌出现次数大于等于b的长度
  • b的长度为5(第一张牌大小为x),循环遍历看看有没有大于b的顺子
#include<bits/stdc++.h>
using namespace std;
char a[105],b[105];
int aa[15];
int main()
{
    while(~scanf("%s %s",a+1,b+1)){
        memset(aa,0,sizeof(aa));
        int la=strlen(a+1),lb=strlen(b+1);
        for(int i=1;i<=la;++i)
            ++aa[a[i]-'0'];
        int ju=0;
        if(lb>=1&&lb<=4){
            for(int i=b[1]-'0'+1;i<=9;++i){
                if(aa[i]>=lb){
                    ju=1;
                    break;
                }
            }
        }else{
            for(int i=b[1]-'0'+1;i+4<=9;++i){
                if(aa[i]&&aa[i+1]&&aa[i+2]&&aa[i+3]&&aa[i+4]){
                    ju=1;
                    break;
                }
            }
        }
        if(ju)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

问题 G: 三角阵

题目描述

把3个相同的小三角形按如下方式连接起来就形成了一个一级三角阵。

在这里插入图片描述
我们把位于顶端的小三角形标记为T,位于左端的小三角形标记为L,位于右端的小三角形标记为R。 把3个一级三角阵按同样的方式连接起来就形成了一个二级三角阵。
在这里插入图片描述

我们为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。 把3个二级三角阵按同样的方式连接起来就形成了一个三级三角阵。
在这里插入图片描述

同样地为顶端的三角阵的标记添加前缀T,为左端的三角阵的标记添加前缀L,为右端的三角阵的标记添加前缀R。 依次类推,可以构建一个N级三角阵。 如果两个小三角形有公共点,则认为这两个小三角形相邻,可以一步到达。 你的任务是求从一个小三角形走到另一个小三角形至少需要多少步。

输入

第一行是三角阵的等级N(N≤30)。
第二行和第三行都是一个长度为N的字符串,由大写字母“L”、“R”、“T”组成,表示两个小三角形的标号。

输出

输出 1 个数,表示从一个小三角形走到另一个小三角形所需的最少步数。

样例输入 Copy

3
TRL
RLR

样例输出 Copy

5

提示

从“TRL”出发,途经“TRR”、“RTT”、“RTL”、“RLT”,最后到达“RLR”,一共需要5步。

思路

先找到起点和终点所在三角形不同的那一层,然后可以发现最短路径有两条
在这里插入图片描述
最短路径就是1+2和3+4+5中最短的那一个
重要的就是路径1,2,3,5的求法(但是不知道如何解释这个求法…只能略过了…π__π)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
    int n,st=0;
    char a[35],b[35];
    scanf("%d%s%s",&n,a+1,b+1);
    for(int i=1;i<=n;++i){
        if(a[i]!=b[i]){
            st=i;
            break;
        }
    }
    if(st==0){
        printf("0\n");
        return 0;
    }
    ll num=pow(2,n-st-1),ans1=1,ans2;
    for(int i=st+1;i<=n;++i){
        if(a[i]!=b[st])
            ans1+=num;
        if(b[i]!=a[st])
            ans1+=num;
        num>>=1;
    }
    num=pow(2,n-st-1);
    ans2=pow(2,n-st)+1;
    for(int i=st+1;i<=n;++i){
        if(a[i]==a[st]||a[i]==b[st])
            ans2+=num;
        if(b[i]==b[st]||b[i]==a[st])
            ans2+=num;
        num>>=1;
    }
    printf("%lld\n",min(ans1,ans2));
    return 0;
}

问题 H: 星象仪

题目描述

在寂寞的夜里,星象仪是非常浪漫的东西。但是,你作为一个精神稍微有点不太正常的Geek,把原本正常的星象仪改造得像电报发送器一样。当然,你这个的构造还要更加奇葩一点。具体来说,你的星象仪是一棵满二叉树,二叉树的节点都是有两个输入端和一个输出端的AND 门或者OR 门。它们输入和输出的信号都是只是0 或者1。它们会接受子节点的输出信号,然后将这两个信号进行AND 运算或者OR 运算作为自己的输出。然后,根节点的输出信号就是整个星象仪的输出信号。叶节点的输入信号是由你来调整的,如果二叉树有K 层,那么你显然有2K个输入信号可以调整。调整一次当然只能改变一个输入信号。根据你的设定,在一开始所有的输入端的输入信号都是0。现在你希望用星象仪得到一串信号,为此,你需要不停地调整输入。
在这里插入图片描述

假定你想要用上图中的星象仪得到输出信号000111,一种可行的方案是0001→0011→1100→1111→1010→0101,但是这样你要调整14 次输入信号。更加方便的方式是0000→0000→0000→0101→0101→0101,这样你总计只需要调整2次输入信号。 由于调整输入信号是一件非常麻烦的事情,现在你希望知道对于一台给定的星象仪,如果想要得到一串给定的信号,至少需要调整多少次输入。

输入

输入文件包含多组测试数据。第一行有一个整数T,表示测试数据的组数。
每组测试数据的第一行是一个正整数 N,表示输入信号的数目。保证N 是2 的整数次幂。
第二行含有一个由 0 和1 组成的字符串S,表示你想要得到的信号。
第三行包含 N – 1 个整数,按照层次遍历顺序给出满二叉树的每个节点。整数只会是0或者1。0 表示二叉树的这个位置是一个OR 门,1 表示是一个AND 门。(T≤100,N≤8192,S的长度在10000 之内)

输出

对于每组测试数据,在单独的一行内输出结果。

样例输入

2
4
010101
0 0 0
4
111111
1 1 1

样例输出

5
4

思路

注:二叉树的层序遍历保存在一个数组中,节点i的左右子树为i×2 和 i×2+1

当信号仪的信号第一次改变后,后续的变化只需要调整一个输入就可以
所以只需要求出信号第一次从0变成1所需的输入次数再加上后续信号改变次数就是答案
对于信号从初始状态变成1所需的输入次数用递归实现
与门从0变1需要改变两个子节点的输入,或门从0变1需要改变一个子节点的输入…层层递归下去

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int tr[maxn],n;
char s[maxn];
int dfs(int now,int v){
    if(v==0)
        return 0;
    if(now*2>=n){
        if(tr[now])
            return 2;
        return 1;
    }
    if(tr[now])//与门需两边置1
        return dfs(now<<1,v)+dfs(now<<1|1,v);//左移运算符
    else//或门选最小值
        return min(dfs(now<<1,v),dfs(now<<1|1,v));
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        scanf("%s",s+1);
        int ls=strlen(s+1),num=0;
        s[0]='0';
        for(int i=1;i<n;++i){
            scanf("%d",&tr[i]);
        }
        for(int i=1;i<=ls;++i)
            if(s[i]!=s[i-1])
                ++num;
        if(num==0){
            printf("0\n");
        }else{
            printf("%d\n",dfs(1,1)+num-1);
        }
    }
    return 0;
}

问题 I: 网络入侵

题目描述

在人气动漫《某科学的超电磁炮S》第七话中,Level 5的超能力者Railgun(御坂美琴)得知了学园都市的“量产型能力者计划”,决定通过破坏研究所来阻止这个计划。为了在破坏研究所时不被监控装置发现,Railgun需要先关掉学园都市的部分监控装置。
在这里插入图片描述

学园都市的信息网络共有N个节点,这些节点用正整数1…N编号,它们之间通过N-1条地下光缆连接,保证任意两个节点间都是连通的。每条光缆上有一个监控装置,初始时有些监控装置是开着的,有些则是关闭的。 Railgun现在正在电话亭入侵学园都市的网络,她可以按照如下的方式操作监控装置:选择两个不同的网络节点a,b,将节点a到节点b的路径上的监控装置状态都取反(开着的装置关掉、关闭的装置打开)。注意这里的路径是指节点a,b间不经过重复节点的那一条唯一路径。
在这里插入图片描述

有些监控装置必须关掉后,才能保证Railgun不被发现,她希望经过若干次操作后这些装置处于关闭状态。你的任务就是计算最少需要多少次操作达到目的。

输入

输入共N+2行,第一行包含一个正整数N
第二行包含一个长度为N-1的01字符串,第i个字符表示开始时第i条光缆上的监控装置是否开着,1表示开着,0表示关闭。
第三行包含一个长度为N-1的01字符串,第i个字符表示第i条光缆上的监控装置对于Railgun来说是否必须关闭,1表示必须关闭,0表示无需关闭。
下面N-1行,每行包含两个正整数a,b(1≤a,b≤N,a≠b)表示节点a和节点b间存在一条光缆。(N≤100,000)

输出

输出共一行,包含一个非负整数,表示到达Railgun的目的所需的最少操作次数

样例输入

5
1110
0111
1 2
1 3
2 4
2 5

样例输出

1

提示

在这里插入图片描述
上图中,对于Railgun来说必须关闭的监控装置被标为红色,每条道路旁边的01数字表示这条光缆上监控装置的状态。最优的方案是选择节点3和4,虚线包围的道路上的装置改变状态(全部从打开变成关闭)。

思路

如果装置是关闭的又需要它关闭,则不需要操作…不进行连边
如果装置是开启的又需要它关闭,则连边,值为1
其他情况连边,值为0
循环遍历所有节点,如果没有被标记过就以该节点为根节点dfs,对遍历过的节点进行标记…
如果一个节点有偶数个子节点(n)路径需要操作,对答案的贡献就是n/2
如果一个节点有奇数个子节点(n)路径需要操作,对答案的贡献就是n/2,剩下的一条边延伸给父节点
贴个图
黑色是无关装置,红色是要操作的装置,绿色是操作的路径
在这里插入图片描述
链式前向星详解

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct node{
    int to,v,next;
}edge[maxn<<2];
int head[maxn],vis[maxn],cnt=0,ans=0;
char a[maxn],b[maxn];
void add(int a,int b,int c){//前向星存图
    edge[++cnt].to=b;
    edge[cnt].v=c;
    edge[cnt].next=head[a];
    head[a]=cnt;
}
int dfs(int now,int ju){//now当前节点,ju与父节点连边是否需要操作
    vis[now]=1;
    int num=0;
    for(int i=head[now];i!=-1;i=edge[i].next){
        int to=edge[i].to,v=edge[i].v;
        if(vis[to])
            continue;
        if(dfs(to,v))
            ++num;
    }
    ans+=num/2;
    return num%2==1||ju;
}
int main()
{
    int n;
    scanf("%d%s%s",&n,a+1,b+1);
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;++i){
        int from,to;
        scanf("%d %d",&from,&to);
        if(a[i]=='1'&&b[i]=='1')
            add(from,to,1),add(to,from,1);
        else if(a[i]=='1'&&b[i]=='0'){
            add(from,to,0),add(to,from,0);
        }else if(a[i]=='0'&&b[i]=='0'){
            add(from,to,0),add(to,from,0);
        }
    }
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;++i){
        if(vis[i]==0&&dfs(i,0)){
            ++ans;
        }
    }
    printf("%d\n",ans);
    return 0;
}

问题 J: 左右手

题目描述

「十代目,在吗?」
「啊~,Reborn布置的数学题,完全不知道怎么做啊!」
「没关系,交给我吧!」
Reborn 给了一个数字 X ,27 可以给 N 个数字指定值,使得这 N 个数字进行或操作以后,再乘上任意一个数字的结果等于 X, 问有多少种指定方法。
「十代目,我已经算出来了,就这样这样这样…」
聪明的 59 已经解决了问题,那你呢?

输入

第一行两个整数 X (1≤X≤109) 和 N (1≤N≤109)

输出

打印一个整数,表示所求答案(结果对1000000007取模)。

样例输入

2 2

样例输出

6

提示

有以下6种方法, 分别为 ( 第一个数字的值, 第二个数字的值, 乘数 ) :
(0, 2, 1)
(2, 2, 1)
(1, 0, 2)
(0, 1, 2)
(1, 1, 2)

思路

题目大意:n个数字进行或操作乘某一个数字等于x,求这n个数字的不同组合数
也就是求n个数字或操作后成为x因子的组合数
我们就枚举x的因子i,求n个数或操作后等于i的不同组合,全部累加
或操作考虑二进制
如果i的二进制某一位为0那么这n个数中这一位不能有1 --只有一种情况
如果i的二进制某一位为1那么这n个数中这一位必须有1
可能有一个1(C(n,1))可能有两个1(C(n,2))…可能有n个1(C(n,n))
所以所有的可能为(C(n,1)+C(n,2)+C(n,3)+…C(n,n) )化简得2^n-1
用到的是一个组合数求和的公式
在这里插入图片描述

如果i的二进制有多个1,因为每个位的选择都是独立的,那就是多个2^n-1相乘
如果i的二进制有num个1,则n个数或操作后等于i的组合为
在这里插入图片描述
把x的所有因子的组合数相加就是答案
快速幂详解

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1000000007;
ll pow_(ll a,ll b){//快速幂
    ll ans=1;
    while(b){
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    ll x,y,n,ans=0;
    scanf("%lld %lld",&x,&n);
    y=sqrt(x);
    for(int i=1;i<=y;++i){
        if(x%i==0){//i是因子
            int j=x/i;//j也是因子
            int now=i,num=0;
            while(now){//求i的二进制形式1的个数
                if(now&1)
                    ++num;
                now>>=1;
            }
            ans=(ans+pow_(pow_(2,n)-1,num))%mod;
            if(i==j){//因子重复
                continue;
            }
            now=j,num=0;
            while(now){//求j的二进制形式1的个数
                if(now&1)
                    ++num;
                now>>=1;
            }
            ans=(ans+pow_(pow_(2,n)-1,num))%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
发布了149 篇原创文章 · 获赞 39 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43984169/article/details/104735980