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)2i个1个1,即二进制表示下后i位为1,其余位为0的数。给定一个正整数n,求 ∏ i = 1 n ∏ j = 1 n A i & A j ∏_{i=1}^n∏_{j=1}^nAi\&Aj ∏i=1n∏j=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 T≤100, 1≤n≤64。
题意分析
我们要求的是对于一个
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 Ai | 1 | 2 | … | N |
---|---|---|---|---|
1 | 1 | 1 | … | 1 |
2 | 1 | 11 | … | 11 |
… | 1 | 11 | … | j |
N | 1 | 11 | … | 2 N − 1 2^{N}-1 2N−1 |
化为十进制:
A j A_j Aj \ A i A_ i Ai | 1 | 2 | … | N |
---|---|---|---|---|
1 | 1 | 1 | … | 1 |
2 | 1 | 3 | … | 3 |
… | 1 | 3 | … | j |
N | 1 | 3 | … | 2 N − 1 2^{N}-1 2N−1 |
表内的关系可以总结得到:
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)=(2N−1)∗1+(2N−3)∗3+...+3∗(2N−1−1)+2N−1
代码:
#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;
}
题目描述
小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} T≤3×105, n≤1018。输入数据量可能较大,建议使用较快的读入方式。
题意分析:
就是很明晰的,对一串连续的数求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)=2∗lowbit(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))=2∗time(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}
2N−1],都满足
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)=2N−1
有了上面的两点那么
l
o
w
b
i
t
(
x
)
∈
[
1
,
2
N
−
1
]
lowbit(x)\in[1,2^{N-1}]
lowbit(x)∈[1,2N−1]满足
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=2N−1,最后最特殊的
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)∗2N−1(未取模)
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} 2N−2 | 2 N − 1 2^{N-1} 2N−1 | 2 N 2^N 2N |
---|---|---|---|---|---|---|---|---|---|
出现次数time | 2 N ∗ 2 − 1 2^{N}*2^{-1} 2N∗2−1 | 2 N ∗ 2 − 2 2^{N}*2^{-2} 2N∗2−2 | 2 N ∗ 2 − 3 2^{N}*2^{-3} 2N∗2−3 | 2 N ∗ 2 − 4 2^{N}*2^{-4} 2N∗2−4 | 2 N ∗ 2 − 5 2^{N}*2^{-5} 2N∗2−5 | … | 2 N ∗ 2 − ( N − 1 ) 2^{N}*2^{-(N-1)} 2N∗2−(N−1) | 2 N ∗ 2 − N 2^{N}*2^{-N} 2N∗2−N | 1 |
心疼自己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;
}
题目描述
小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×(j−i)−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} sj−1交换,直到移动到i位置所需的总移动次数)。求将序列串中同种字符划分到一起的最小花费(如字符串AGACG
,将其变成AAGGC
或GGAAC
或CAAGG
…都是合法的,最小花费是2:AAGGC
)。
输入描述:
输入一行,一个字符串s,表示题目描述所述的DNA序列串。
输出描述:
输出一个整数,表示将s中同种字符划分到一起的最小花费。
示例1
输入
AGACG
输出
2
备注:
字符串长度n满足 1 ≤ n ≤ 2 × 1 0 5 1\leq n\leq2\times10^5 1≤n≤2×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;
}