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)\)
  • 因而我们有状态转移方程

\[dp[j]=max(dp[j],cnt[j]*(j-i)+dp[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;
    }
    
posted @ 2021-12-15 19:40  Ghaser  阅读(45)  评论(0)    收藏  举报