牛客练习赛54(仍在更新,目前有ABC题)

牛客练习赛54


A乘积
题目描述

A i = ( 00..0 11..1 ⏟ ) 2 i 个 1 \begin{matrix}A_i=(00..0\underbrace{11..1})_2\\\quad\quad\quad\quad i个1\end{matrix} Ai=(00..0 11..1)2i1个1​,即二进制表示下后i位为1,其余位为0的数。给定一个正整数n,求 ∏ i = 1 n ∏ j = 1 n A i & A j ∏_{i=1}^n∏_{j=1}^nAi\&Aj i=1nj=1nAi&Aj
输出答案对998244353取模后的结果。T组数据。

输入描述:

第一行一个整数T,表示数据组数。接下来T行每行一个整数n,含义如题目描述所示。

输出描述:

输出T行,表示每次询问的结果。

示例1

输入

2
2
4

输出

3
1250235

备注:

T ≤ 100 ,   1 ≤ n ≤ 64 T\leq100,\ 1\leq n\leq64 T100, 1n64


题意分析
我们要求的是对于一个 A i A_i Ai都要和 A 1 , A 2 , . . . , A n A_1,A_2,...,A_n A1,A2,...,An做按位与操作&,并将所有的 A i & A j A_i\&A_j Ai&Aj乘在一起。

我们很容易发现一个规律 A i & A j = m i n ( A i , A j ) A_i\&A_j=min(A_i,A_j) Ai&Aj=min(Ai,Aj),可以话为二进制自己推一下。
那么我们可以得到一个关于 A i & A j A_i\&A_j Ai&Aj的表格:
二进制:

A j A_j Aj \ A i A_ i Ai12N
1111
211111
111j
N111 2 N − 1 2^{N}-1 2N1

化为十进制:

A j A_j Aj \ A i A_ i Ai12N
1111
2133
13j
N13 2 N − 1 2^{N}-1 2N1

表内的关系可以总结得到:
a n s ( N ) = ( 2 N − 1 ) ∗ 1 + ( 2 N − 3 ) ∗ 3 + . . . + 3 ∗ ( 2 N − 1 − 1 ) + 2 N − 1 ans(N)=(2N-1)*1+(2N-3)*3+...+3*(2^{N-1}-1)+2^{N}-1 ans(N)=(2N1)1+(2N3)3+...+3(2N11)+2N1

代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int Mod = 998244353;
ull qpow(ull a,ull b){
    ull ans = 1;
    while(b){
        if(b&1)ans = ans*a%Mod;
        a = a*a%Mod;
        b>>=1;
    }
    return ans%Mod;
}
int main(){
    int T,a;
    ull ans,num,now;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&a);
        ans = 1;
        now = 0;
        for(int i = a;i >= 1;i--){
           num = 2*i-1;
           now = (now<<1|1)%Mod;
           ans = ans*qpow(now,num)%Mod;
        }
        cout << ans <<endl;
    }
    return 0;
}

B求和

题目描述

小a学习了树状数组之后,对 lowbit \text{lowbit} lowbit很感兴趣,于是出了一道题。
给定非负整数n。记 lowbit ( x ) \text{lowbit}(x) lowbit(x)为x的二进制表示下最低位的1所对应的值,如,某个数x最低位的1分别在第1,2,3位时, lowbit ( x ) \text{lowbit}(x) lowbit(x)分别是1,2,4。求 ∑ i = 0 2 n lowbit ( i ) \sum_{i=0}^{2^n}\text{lowbit}(i) i=02nlowbit(i)
输出答案对998244353取模后的结果。T组数据。

输入描述:

第一行一个整数T,表示数据组数。接下来T行每行一个整数n,含义如题目描述所示。

输出描述:

输出T行,表示每次询问的结果。

示例1

输入

2
2
4

输出

8
48

说明

n=2时, A n s = ∑ i = 0 4 lowbit ( i ) = 0 + 1 + 2 + 1 + 4 = 8 Ans=\sum_{i=0}^{4}\text{lowbit}(i)=0+1+2+1+4=8 Ans=i=04lowbit(i)=0+1+2+1+4=8

备注:

T ≤ 3 × 1 0 5 ,   n ≤ 1 0 18 T\leq3\times10^5,\ n\leq10^{18} T3×105, n1018。输入数据量可能较大,建议使用较快的读入方式。


题意分析:
就是很明晰的,对一串连续的数求lowbit和对于lowbit函数就不过多解释了,有想了解的戳这里

我们关注一下lowbit的作用:取最低一位1。
我们可以知道在递增过程中,我们要明确两点:
1)二进制是满二进一的。那导致我们的lowbit(x)在一般情况下是满足 l o w b i t ( i ) = 2 ∗ l o w b i t ( j ) lowbit(i)=2*lowbit(j) lowbit(i)=2lowbit(j)(在二进制中表现为i的最低位的 1 1 1出现在j最低位的 1 1 1的右侧相邻的位置例如 001000 001000 001000 000100 000100 000100)的对于每个lowbit值出现次数函数time,有 t i m e ( l o w b i t ( j ) ) = 2 ∗ t i m e ( l o w b i t ( i ) ) time(lowbit(j))= 2*time(lowbit(i)) time(lowbit(j))=2time(lowbit(i)),那么我们可以很清楚的发现, l o w b i t ( i ) ∗ t i m e ( i ) = l o w b i t ( j ) ∗ l o w b i t ( j ) = K lowbit(i)*time(i) = lowbit(j)*lowbit(j)=K lowbit(i)time(i)=lowbit(j)lowbit(j)=K且,在 x x x ∈ \in [1, 2 N − 1 2^{N-1} 2N1],都满足 l o w b i t ( x ) ∗ t i m e ( x ) = K lowbit(x)*time(x) = K lowbit(x)time(x)=K
2)二进制从 2 0 2^0 20开始增加,每一次+1都会表现在 2 0 2^0 20的对应位上面。在二进制的 2 0 2^0 20位的序列:
0   1   0   1   0   1   0   1   0...   1   0   1   0 0~1~0~1~0~1~0~1~0...~1~0~1~0 0 1 0 1 0 1 0 1 0... 1 0 1 0
很容易发现 1 1 1每两个出现一次(不考虑,lowbit(0)的一个 0 0 0的话,就是每两位的第一位为 1 1 1)那么很明显 t i m e ( 1 ) = 2 N − 1 time(1) =2^{N-1} time(1)=2N1

有了上面的两点那么 l o w b i t ( x ) ∈ [ 1 , 2 N − 1 ] lowbit(x)\in[1,2^{N-1}] lowbit(x)[1,2N1]满足 l o w b i t ( x ) ∗ t i m e ( x ) = K = 2 N − 1 lowbit(x)*time(x) = K =2^{N-1} lowbit(x)time(x)=K=2N1,最后最特殊的 l o w b i t ( x ) = x = 2 N lowbit(x)=x=2^{N} lowbit(x)=x=2N的情况也只出现了一次。
那么有 a n s = ( N + 2 ) ∗ 2 N − 1 ans = (N+2)*2^{N-1} ans=(N+2)2N1(未取模)

lowbit(x) 2 0 2^0 20 2 1 2^1 21 2 2 2^2 22 2 3 2^3 23 2 4 2^4 24 2 N − 2 2^{N-2} 2N2 2 N − 1 2^{N-1} 2N1 2 N 2^N 2N
出现次数time 2 N ∗ 2 − 1 2^{N}*2^{-1} 2N21 2 N ∗ 2 − 2 2^{N}*2^{-2} 2N22 2 N ∗ 2 − 3 2^{N}*2^{-3} 2N23 2 N ∗ 2 − 4 2^{N}*2^{-4} 2N24 2 N ∗ 2 − 5 2^{N}*2^{-5} 2N25 2 N ∗ 2 − ( N − 1 ) 2^{N}*2^{-(N-1)} 2N2(N1) 2 N ∗ 2 − N 2^{N}*2^{-N} 2N2N1

心疼自己Corner case:N=0未考虑超时了好几次
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Mod = 998244353;
inline ll read()   //输入外挂,输入量大时,比scanf效率高   
{  
    ll x=0,f=1;  
    char ch=getchar();  
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}  
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}  
    return x*f;  
}
inline void write(ll x){
	if (x < 0) x = ~x + 1, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
ll qpow(ll a,ll b){
     //a = a%Mod;
    ll ans = 1;
    while(b){
        if(b&1)ans = (ans*a)%Mod;
        a = a*a%Mod;
        b>>=1;
    }
    return ans;
}
int main(){
    ll a,ans,T;
    T = read();
    while(T--){
        a = read();
        if(a==0)
            ans = 1;
        else {
            ans = qpow(2,a-1)%Mod;
            ans = (ans*((a+2)%Mod)%Mod)%Mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

C排序

题目描述

小a有一个DNA序列串,强迫症的小a看它不顺眼,想将它排好序。
给定长为n的DNA序列串s(仅由A,T,G,C最多四种字符构成)。你可以进行任意次如下操作:任选两个位置i,j( i < j i \lt j i<j ),交换这两个字符 s i , s j s_i,s_j si,sj,花费为 2 × ( j − i ) − 1 2\times(j-i)-1 2×(ji)1 (即:将 s i s_i si​不断与 s i + 1 s_{i+1} si+1​交换,直到移动到j位置,再将 s j s_j sj​不断与 s j − 1 s_{j-1} sj1交换,直到移动到i位置所需的总移动次数)。求将序列串中同种字符划分到一起的最小花费(如字符串AGACG,将其变成AAGGCGGAACCAAGG…都是合法的,最小花费是2:AAGGC)。

输入描述:

输入一行,一个字符串s,表示题目描述所述的DNA序列串。

输出描述:

输出一个整数,表示将s中同种字符划分到一起的最小花费。

示例1

输入

AGACG

输出

2

备注:

字符串长度n满足 1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq2\times10^5 1n2×105


题意分析:
我们要将ATCG分在一起,求其中的交换最小次数。

我们在仔细读过题后发现,其实我们的交换类似于冒泡排序的交换过程,既然想到冒泡而且还使ATCG分区块,那么不经想到吧ATCG看做4个数,求其冒泡排序的次数,就是交换次数。我们只需要确定24种ATCG的大小的排序,再求对应冒泡排序的交换次数,取最小值即可。

排序我用了全排列的函数,求冒泡排序的次数就是逆序对。结合即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string str;
int A = 0,T = 0,G = 0,C = 0;
ll ans[30]={0};
char per[]={"ACGT"},perr[30][10];
void sum(int num,char c){
    for(int i = 0;perr[num][i]!=c&&perr[num][i]!='\0';i++){
        if(perr[num][i]=='A')ans[num]+=A;
        else if(perr[num][i]=='C')ans[num]+=C;
        else if(perr[num][i]=='G')ans[num]+=G;
        else ans[num]+=T;
    }
}
int main(){
    cin >> str;
    int len = str.length();
     for(int i = 0;i <= 4;i++)perr[1][i] = per[i];
       int cnt = 1;
    while(next_permutation(per,per+4)){
        ++cnt;
       for(int i = 0;i <= 4;i++)perr[cnt][i] = per[i];
    }
    for(int i = 0;i < len;i++){
        if(str[i]=='A')A++;
        else if(str[i]=='T')T++;
        else if(str[i]=='G')G++;
        else C++;
        for(int j = 1;j <= cnt;j++)sum(j,str[i]);
    }
    ll Min = 1e18;
    for(int i = 1;i <= cnt;i++){
       Min =min(Min,ans[i]);
    }
    printf("%lld\n",Min);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值