K.Bit位运算

题意:

0到r中,选择一个数x进行n次操作,所得最大值是多少。

  • 操作1a:x=x & a
  • 操作2a:x=x | a
  • 操作3a:x=x ^ a q个r

思路:

  • 通过与、或、异或运算性质可以知道,最后每一位能变成什么样子,取决于最后一个与运算(若没有与运算则就是所有操作累计起来的结果)及以后操作累计起来的结果,所以我们需要一个变量来记录最终全部操作进行完每一位的情况,最后我们需要在那些为0的位置上贪心地从大到小选择一些位置,使得这些位置为1组成的数进行完所有操作后值最大,然后还需要记录经过这些操作以后的最大值是多少,改变的时候只需要改变最大值该位为1而累计起来的结果为0的位置需要置为1

  • 做法:二进制拆位,贪心。
  • 这其实是一个非常套路的题目,由于这 n 个操作是固定的,而且 与二进制有关,所以不妨从二进制的角度去考虑。 我们可以预先处理出二进制第 i 位初始为 1 或 0 时,经过这 n 次操作之后它的变化。然后对于每一个询问,从二进制的高位到 低位枚举,判断数 x 的当前位应该为 1 还是 0,如果当前位初始 为 0 时能得到 1,则优先将当前位置为 0。若当前位初始为 1 时 能得到 1,且将当前位置为 1 后仍不超过 r 的限制,也将当前位 置为 1。这么贪心选取出的 x 一定满足它经过这 n 个操作之后 得到的数是最大的。 时间复杂度:O(30 × (n + q))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int b=INT_MAX,b1=0;
    int n,m;
    int main()
    {
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int op,x;
        cin>>op>>x;
        if(op==1)b&=x,b1&=x;
        else if(op==2)b|=x,b1|=x;
        else b^=x,b1^=x;
    }
    while(m--)
    {
        int r;
        cin>>r;
        int ans=0;
        for(int i=30;i>=0;i--)
        {
            if(b>>i&1&&(b1>>i&1)==0)
            {
                if((ans|(1<<i))<=r)
                {
                    ans|=1<<i;
                }
            }
        }
        cout<<ans<<'\n';
    }
    return 0;
    }

G. Red Black Tree

题目:

黑色叶子下面两个必须是黑色,相邻都为黑色的叶子上面必须为黑色。n层红黑树,给k个黑色节点,补充后符合标准,这时红黑树中黑色叶子有多少个。

思路:

做法: 首先可以发现最终状态时一定是若干个不相交的三角形,且一个 底长为 m 的三角形中叶子个数为 m×(m+1) 2 。也就是说,我们只 要知道每个三角形的底长就可以算出最后的答案。 于是我们可以记录出每个点扩散到第 n 层的区间,然后对这些 区间取并集后的每一个不相交区间分别计算答案然后相加。 求区间并集可以使用差分 + 前缀和。 时间复杂度:O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
int a[2000006];
int main(){
    int n,k;
    cin>>n>>k;
    for (int i = 1; i <=k ; ++i) {
        int x,y;
        cin>>x>>y;
        a[y]++;
        a[y+n-x+1]--;
    }
    for (int i = 1; i <=n ; ++i) {
        a[i]+=a[i-1];
    }
    int ans=0,ant=0;
    for (int i = 1; i <=n ; ++i) {
        if(a[i])ant++;
        else ans+=(ant+1)*ant/2,ant=0;
    }
    ans+=(ant+1)*ant/2,ant=0;
    cout<<ans<<endl;
}

H - Beautiful String

题意:

简要题意:给出一个字符串 s,定义一个长度为 n 且只含有不同 字符的字符串 t 是好字符串需要满足以下两个条件之一:

  • 1、t1 出现在串 s 中且 t 串中的字符的大小是严格递增的。
  • 2、t 串中每个字符都在 s 中出现过。
  • 求不同字符串 t 的个数。

    思路:

    做法:简单的组合数学。

  • 考虑第一种条件,我们可以记录下 s 串中出现过的字母,那么我 们可以枚举每一个出现的字母,这里我们将比当前字母大的字母 种类数记作为 p,那么这部分的答案是 Cn−1(p) 。
  • 考虑第二种条件,我们可以记录下 s 串中出现过的字母,这里将 出现过的字母种类数记作为 x,那么这部分的答案是 An(x)。 但是若把这两部分的答案相加,则会多算同时满足两个条件的个 数,因此可以简单的容斥一下,将这部分的答案减掉。同时满足 两个条件的个数为 Cn(x) 。
  • 为了避免取模而将字符集限制在了 18,不过注意要开 long long。 时间复杂度:O( ∑|s| + 18 × T)

    代码:

    
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll a,b,c;
    ll AA(int n,int m){
    ll aa=1;
    while (m--){
        aa*=n--;
    }
    return aa;

} ll CC(ll n,ll m){ ll aa=1; int k=m; while (m--){ aa=n--; } ll bb=1; while (k){ bb=k; k--; } ll gg; gg=aa/bb; return gg;

} int main(){ int t; cin>>t; while (t--){ string s; int nn; cin>>s>>nn; a=0,b=0,c=0; set<char>st; for (auto i:s) { st.insert(i); } int k = st.size(); if(k>=nn){ a= AA(k,nn); c= CC(k,nn); }

    for (int i = 0; i <18 ; ++i) {
        if (st.count(char('a' + i)))
            if(17-i>=nn-1){
                b+=CC(17-i,nn-1);
            }
    }
    cout<<a+b-c<<endl;
}

}


# [I. Digit Problem](https://codeforces.com/group/L9GOcnr1dm/contest/422378/problem/I)
## 题意:
x和y二进制表示含有a个1,b个0,z含有c个1,z=x-y成立时,输出一对x,y;
## 思路:
- 若 a > 0 且 b > 0 时,只有当 0 ≤ c ≤ a + b − 1 时是有解的。
- 下面给出一种 a > 0 且 b > 0 时的构造方式:
1. -  将 x 固定为 111...1111000...000。
2. - 若 c ≤ a 时,y 可以这么构造:
 - 若 a < c ≤ a + b − 1 时,y 可以这么构造:(a-c)*1 0 c*1 (b-1)*0
 - 最后对于 a = 0 或 b = 0 的情况进行特判即可。
时间复杂度:O(a + b)
## 代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
    int a,b,c;
    cin>>a>>b>>c;
    if(c>=a+b||a*b==0&&c){
        cout<<"-1\n";return 0;
    }
    if(c==0)
        cout<<string(b,'0')<<string(a,'1')<<'\n';
    else if(c<=b)
        cout<<string(b-c,'0')<<'1'<<string(c,'0')<<string(a-1,'1')<<'\n';
    else
        cout<<'1'<<string(b-1,'0')<<string(c-b,'1')<<'0'<<string(a-c+b-1,'1')<<'\n';
    cout<<string(b,'0')<<string(a,'1')<<'\n';
    return 0;
}

I. Digit Problem

题意:

x和y二进制表示含有a个1,b个0,z含有c个1,z=x-y成立时,输出一对x,y;

思路:

  • 若 a > 0 且 b > 0 时,只有当 0 ≤ c ≤ a + b − 1 时是有解的。
  • 下面给出一种 a > 0 且 b > 0 时的构造方式:
      • 将 x 固定为 111...1111000...000。
      • 若 c ≤ a 时,y 可以这么构造:
      • 若 a < c ≤ a + b − 1 时,y 可以这么构造:(a-c)1 0 c1 (b-1)*0
      • 最后对于 a = 0 或 b = 0 的情况进行特判即可。 时间复杂度:O(a + b)

        代码:

        #include<bits/stdc++.h>
        using namespace std;
        int main(){
        int a,b,c;
        cin>>a>>b>>c;
        if(c>=a+b||a*b==0&&c){
        cout<<"-1\n";return 0;
        }
        if(c==0)
        cout<<string(b,'0')<<string(a,'1')<<'\n';
        else if(c<=b)
        cout<<string(b-c,'0')<<'1'<<string(c,'0')<<string(a-1,'1')<<'\n';
        else
        cout<<'1'<<string(b-1,'0')<<string(c-b,'1')<<'0'<<string(a-c+b-1,'1')<<'\n';
        cout<<string(b,'0')<<string(a,'1')<<'\n';
        return 0;
        }
posted on 2023-01-19 16:39  IR101  阅读(6)  评论(0编辑  收藏  举报  来源