Codeforces Round 940 (Div. 2) and CodeCraft-23 题解

Codeforces Round 940 (Div. 2) and CodeCraft-23 题解

题目链接

A. Stickogon 贪心

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define ff first
    #define ss second
    #define pb push_back
    #define all(u) u.begin(), u.end()
    #define endl '\n'
    #define debug(x) cout<<#x<<":"<<x<<endl;
    
    typedef pair<int, int> PII;
    typedef long long LL;
    const int inf = 0x3f3f3f3f;
    const int N = 1e5 + 10, M = 105;
    const int mod = 1e9 + 7;
    const int cases = 1;
    
    void Showball(){
       int n;
       cin>>n;
       vector<int> a(101);
       for(int i=0;i<n;i++){
         int x;
         cin>>x;
         a[x]++;
       }
       int ans=0;
       for(int i=1;i<=100;i++) ans+=a[i]/3;
       cout<<ans<<endl;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cout.tie(nullptr);
        int T=1;
        if(cases) cin>>T;
        while(T--)
        Showball();
        return 0;
    }

B. A BIT of a Construction 贪心

题意:给你 \(n\)\(k\),构造出一个长度为 \(n\) 的数组,使得数组之和为 \(k\),并且使数组或起来的结果的1最多。

思路:为了让1的数量最多,我们直接取最接近 \(k\)\(2^i-1\) 的数即可,剩下的补充即可。

    void Showball(){
       int n,k;
       cin>>n>>k;
       if(n==1) return cout<<k<<endl,void();
       int t=__lg(k);
       int sum=(1<<t)-1;
       cout<<sum<<" "<<k-sum<<" ";
       if(n==2) return cout<<endl,void();
       for(int i=3;i<=n;i++) cout<<0<<" \n"[i==n];
    }

C.How Does the Rook Move? DP|递推

题意:在一个 \(n\times n\) 的棋盘上放置棋子,若棋子坐标为 \((r,c)\),则 \(r\) 行 和 \(c\) 列的所有位置都不能再放置棋子,你和电脑对战,电脑每次会对称复制你的下法(将棋子放置到\((c, r)\) ) 。若你下在对称轴上,电脑跳过此回合,直到无法落子为止,求出棋盘终局有多少种情况。

思路:首先我们注意到,棋子一共只有两种走法,落在对称轴和非对称轴的方式。

并且棋子位置并不重要,我们可以平移。那么对于落在对称轴的情况,棋盘会变成 \((n-1)\times (n-1)\)

,只有一种情况。对于落在非对称轴的情况,期盼会变成 \((n-2)\times (n-2)\),一共有 \(2\times(n-1)\) 种情况。则显然可以得到递推式 \(f[i]=f[i-1]+2*(n-1)*f[i-2]\)

对于一开始的落子情况,我们只需要维护好棋盘规模即可。

    int f[N];
    void init(){
       f[0]=1;
       f[1]=1;
       for(int i=2;i<N;i++){
          f[i]=(f[i-1]+2LL*(i-1)*f[i-2])%mod;
       }
    }
    void Showball(){
       int n,k;
       cin>>n>>k;
       while(k--){
          int x,y;
          cin>>x>>y;
          if(x==y) n--;
          else n-=2;
       }
       int ans=f[n];
       cout<<ans<<endl;
    }
    

D. A BIT of an Inequality 拆位+算贡献+DP

题意:求出满足下列条件的三元组 \((x,y,z)\)的数量:

\(1\leq x\leq y\leq z \leq n\),

\(f(x,y)\)\(f(y,z) > f(x,z)\)

其中 \(f(l,r)=a_l\)\(a_{l+1}\) ⊕ ... ⊕ \(a_r\)

思路:

看到异或数数题,经典的拆位+算贡献,前天小白月赛F题刚做过,这题稍微难一些。

根据异或的性质,右边比左边多异或一个 \(a_y\),值变小。让我们去计算这种情况的数量。

根据2进制的性质,最高位1的影响会超过后面所有1的影响,因此我们只需要处理 \(a_y\) 的最高位 \(1\) .

设最高位 \(1\)\(t\),要满足值变小,说明区间其他数异或和第 \(t\) 位要为1。即区间其他数第 \(t\)\(1\)

数量之和要为奇数。

问题转变成求出维护左右两每一位奇/偶个1的区间数量。

不妨定义 \(s1_{i,j,0/1}\)表示从 \(i\) 个开始往前选 第 \(j\) 位 有 奇/偶个 \(1\) 的区间数

如果当前位为 \(1\) , 那么奇区间和偶区间数量会互换,并且当前位自己构成一个奇区间。

\(s1[j][i][1]=s1[j-1][i][0]+1; s1[j][i][0]=s1[j-1][i][1];\)

反之,奇区间和偶区间数量不会互换,当前位自己构成一个偶区间。

\(s1[j][i][1]=s1[j-1][i][1]; s1[j][i][0]=s1[j-1][i][0]+1;\)

同理维护出从 \(i\) 个开始往后选 第 \(j\) 位 有 奇/偶个 \(1\) 的区间数

最后拆位,枚举 \(a_y\) 将贡献累加即可。


    int s1[N][31][2];//从i个数往前选第j位 奇/偶个1的区间数
    int s2[N][31][2];//从i个数往后选第j位 奇/偶个1的区间数
    void Showball(){
       int n;
       cin>>n;
       vector<int> a(n+1);
       for(int i=1;i<=n;i++) cin>>a[i];
       for(int i=0;i<=n+1;i++){
          for(int j=0;j<=30;j++){
             s1[i][j][0]=s1[i][j][1]=0;
             s2[i][j][0]=s2[i][j][1]=0;
          }
       }
       for(int i=0;i<=30;i++){
          for(int j=1;j<=n;j++){
             if(a[j]>>i&1){
                s1[j][i][1]=s1[j-1][i][0]+1;
                s1[j][i][0]=s1[j-1][i][1];
             }else{
                s1[j][i][1]=s1[j-1][i][1];
                s1[j][i][0]=s1[j-1][i][0]+1;
             }
          }
          for(int j=n;j>=1;j--){
             if(a[j]>>i&1){
                s2[j][i][1]=s2[j+1][i][0]+1;
                s2[j][i][0]=s2[j+1][i][1];
             }else{
                s2[j][i][1]=s2[j+1][i][1];
                s2[j][i][0]=s2[j+1][i][0]+1;
             }
          }
       }
    
       LL ans=0;
       for(int i=1;i<=n;i++){
          int t=__lg(a[i]);
          ans=(ans+1LL*s1[i-1][t][1]*(1+s2[i+1][t][0]));
          ans=(ans+1LL*(s1[i-1][t][0]+1)*s2[i+1][t][1]);
       }
       cout<<ans<<endl;
    }
posted @ 2024-04-25 10:37  Showball  阅读(53)  评论(0编辑  收藏  举报