CodeForce div2 Round #757
C-Divan and bitwise operations
-
题意描述:给定一个序列\(a_1,a_2,\cdots,a_n\)共有\(n\)个元素,我们给定\(m\)组数据,每组数据包括\(l,r,x\),分别表示序列中某段连续子序列的起始元素地址,终止元素地址以及序列中所有元素的\(bitwise\ OR\),保证序列中的每一个元素至少都会出现在\(m\)个连续子序列中的某一个,要求求取该长度为\(n\)的序列的所有子序列的\(bitwise \ XOR\)相加之和。
-
已知某个区间\([l,r]\)的\(bitwise\ OR\)的值\(x\),由于题目是位运算,我们考察\(x\)在二进制状态下的每一位,只有两种情况:
- 如果\(x\)的第\(i\)位为\(0\),则表示\([l,r]\)中的每一个元素的第\(i\)位都是\(0\)
- 如果\(x\)的第\(i\)位为\(1\),则表示\([l,r]\)中至少存在一个元素的第\(i\)位是\(1\)
-
考察原序列所有元素的\(bitwise\ OR\),如果完成运算后的第\(i\)位仍然为\(0\),显然对最后的答案无贡献,如果完成运算后的第\(i\)位为\(1\),则显然其对最后答案有贡献,我们假设第\(i\)位出现\(1\)的次数为\(cnt\),则\(0\)的次数为\(n-cnt\),我们下证第\(i\)位对答案的贡献与\(cnt\)无关
-
我们所求的内容是所有子序列的\(bitwise \ XOR\)相加之和,对于每一个子序列,我们可以看作是从\(n\)个元素中选取\(k\)个元素,而\(n\)可以分为\(cnt\)第\(i\)位为1以及\(n-cnt\)第\(i\)位为0两个部分,
- 当我们选取的\(k\)个元素有偶数个第\(i\)位为\(1\)时,他们的异或和第\(i\)位为\(0\),此时对答案一定没有贡献
- 当我们选取的\(k\)个元素有奇数个第\(i\)位为\(1\)时,他们的异或和第\(i\)位为\(1\),对答案有\(2^i\)的贡献
-
因此我们考察对于第\(i\)位的选取方案,因为选取\(1\)的个数只能是奇数,所以我们的情况总数有\(C_{cnt}^1+C_{cnt}^2+\cdots+C_{cnt}^{2t-1}=2^{cnt-1}\),而我们选取\(0\)时对最后的异或结果毫无影响,对于\(n-cnt\)个元素我们可以任意选取,因而有\(2^{n-cnt}\)种情况,所以我们可以计算第\(i\)位对答案的贡献\(2^{cnt-1}*2^{n-cnt}*2^i=2^{n-1}*2^i\),与\(cnt\)的值显然无关,所以我们在计算最终结果时只需要判断整个序列\(bitwise\ OR\)的第\(i\)位是否为1即可
-
考察完第\(i\)位后我们可以推向二进制状态下的所有位,显然应该为\(\sum^n_{i=1}2^{n-1}*2^i*s[i]\),其中\(s[i]\)表示整个序列的\(bitwise\ OR\)在二进制状态下\(i\)位的值,而化简后我们可知\(\sum^n_{i=1}2^{n-1}*2^i*s[i]=2^{n-1}*orsum\),其中\(orsum\)表示整个序列的\(bitwise\ OR\)的值
-
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9+7;
int T,n,m;
// 先找区间交集
// 注意记住的是按位或而不是按位异或
ll qsm(ll a,ll b){
ll tmp=1;
while(b){
if(b&1)tmp=(tmp*a)%mod;
a=a*a%mod;
b/=2;
}
return tmp;
}
int main()
{
cin>>T;
while(T--){
cin>>n>>m;
ll l,r,x;
ll orsum=0;
for(int i=1;i<=m;i++){
cin>>l>>r>>x;
orsum=orsum|x;
}
ll sum=qsm(2,n-1)*orsum%mod;
cout<<sum<<endl;
}
return 0;
}
D1 Divan and Kostomuksha (easy version)
- 代码参考CF某大佬:原代码链接
- 题意描述:给定一个数组\(a\),要求重新排列数组使得\(\sum^n_{i=1}gcd(a_1,a_2,\cdots,a_i)\)的值最大
- 动态规划类题目
- 我们可以注意到,假设我们重新排列数组得到数组\(b\),有前\(i-1\)个元素两两互有整除关系,而第\(i\)个元素与前\(i-1\)个元素中的某一个元素之间并无整除关系,即\(gcd(a_i,a_t)=1,\exist t\in(1,i-1)\),我们在\(i\)后的\(gcd\)的值都为\(1\)。
- 借助整除关系来对本题所需答案进行求解
- 我们先在输入时统计每个元素出现的次数,用\(cnt\)来记录
- 而对于每个数\(i\),有数\(j\)满足\(j\ mod\ i=0\),则\(j\)添加到一个\(gcd=i\)的序列中时贡献也是\(i\),所以我们对于每一个\(i\),将在题目范围内所有有\(j\ mod\ i=0\)的\(j\)的\(cnt\)加到\(i\)的\(cnt\)上。
- 定义\(dp[i]\)表示序列中前缀\(gcd\)最大值为\(i\)的序列所对应的\(\sum^n_{i=1}gcd(a_1,a_2,\cdots,a_i)\)值
- 因而我们有状态转移方程
-
其中要求有\(j\ mod\ i=0\),表示我们可以在一个前缀\(gcd\)最大为\(i\)的序列中将值为\(j\)或其倍数的元素前移,使得前\(cnt[j]\)项的前缀\(gcd\)值均为\(j\),而因为我们在计算第\(i\)项时已经利用了这\(cnt[j]\)项值中的\(i\)对答案做出了贡献,我们此处\(cnt[j]\)乘上的应该是\(j-i\)
-
参考代码
-
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn = 1e5+5; const int up = 5e6+5; int n; int a[maxn]; ll dp[up],cnt[up]; int main(){ cin>>n; ll ans=0; int maxe=0; for(int i=1;i<=n;i++){ cin>>a[i]; maxe=max(maxe,a[i]); cnt[a[i]]++; } for(int i=1;i<=maxe;i++){ for(int j=i*2;j<=maxe;j+=i){ cnt[i]+=cnt[j]; } } dp[1]=cnt[1]; for(int i=1;i<=maxe;i++){ for(int j=i*2;j<=maxe;j+=i){ dp[j]=max(dp[j],cnt[j]*(j-i)+dp[i]); } ans=max(ans,dp[i]); } cout<<ans<<endl; return 0; }