A. Marin and Photoshoot

题目大意

对于给定的$01$串,可以在其中添加若干个 $1$,使得任意区间内, $0$ 的个数不超过 $1$ 的个数。

解题思路

对于本题,我们只关心前后距离不超过2的两个0:

  1. 对于010, 不合法,需要在中间再加一个1
  2. 对于100001不合法,需要在两个0中加入两个1
    即: 每两个0之间,至少需要两个1

Code

void solve() 
{
    int n;	string s;	vector<int> v;
    cin >> n >> s;
        
    for (int i = 0; i < n; i++)
        if (s[i] == '0')	v.push_back(i);
        
    int res = 0;
    for (int i = 1; i < v.size(); i++)
        if (v[i] - v[i - 1] <= 2)	res += 3 - (v[i] - v[i - 1]);
        
    cout << res << endl;
}

B. Marin and Anti-coprime Permutation

解题思路

这道题当时模拟了n = 1,2,3的例子才找到规律
证明略.需要的话请看官方题解
如果n奇数,则符合条件的排列数一定为0
如果n偶数,我们只需在奇数位置上放偶数, 偶数位置上放奇数即可
对于偶数情况,可得出一个公式=> $res = ((n / 2)!)^2$

Code

void solve()
{
    int n; cin >> n;
    if (n & 1)	
    {
        puts("0");
        return;
    } 
    else 
    {
        int m = n / 2, sum = 1;
        for (int i = m; i >= 1; i--)	sum = (sum * i) % mod;  // 求 n/2 的阶乘
        int now = (sum * sum) % mod;  // 求阶乘的平方
        cout << now << endl;
    }
}

C. Shinju and the Lost Permutation (思维)

解题思路

建议看官方题解(其实是懒得描述)
这里只提供一个关键性质:
对于全排列中 $n$ 移动后答案为1的情况只有$[n,.....]$
所以对于给定的数组,其中只能出现一个1,否则就不合法

Code

void solve()
{
    int n;  cin >> n;
    for (int i = 1; i <= n; i++)    cin >> c[i];

    if (count(c + 1, c + 1 + n, 1) != 1) // 只能出现一个1
    {
        puts("NO");
        return;
    }

    vector<int> t, a;
    for (int i = 1; i <= n; i++)  // 把给定的数组变为从1开头,对应于排列【n,...]的情况
    {
        if (c[i] != 1)  t.push_back(c[i]);
        else 
        {
            for (int j = i; j <= n; j++)    a.push_back(c[j]);
            for (auto x : t)    a.push_back(x);
        }
    }

    for (int i = 1; i < a.size(); i++)
        if (a[i] - a[i - 1] > 1) 
        {
            puts("NO");
            return;
        }

    puts("YES");
}

D1. 388535 (Easy Version) (位运算/规律)

题目大意

对于给定的数组 $a$ ,你需要找到一个数 $x$ ,满足 $b_i = a_i \oplus x$后,对 $b$ 排序后的结果为 $[0,1,2,3,4,5...n]$ 。问 $x$ 是多少。

解题思路

我们首先可以列出0~7的二进制
分别为

000  0   我们对于每一位进行查看(垂直): 对于第0位:  对于第1位 
001  1                                  1       0
010  2                                  0       0    
011  3                                  1       1
100  4                                  0       1
101  5                                  1       0
110  6                                  0       1    
111  7                                  1       1

可以发现性质: 任意一位的前缀中,$0$ 的个数一定大于等于 $1$ 的个数。
只有满足这个性质才可以构成$b$数组排序后形成$[0,1,2,3,4,5...n]$这样的连续排列。

所以对于给定的异或后的结果,我们只需要对它的每一位0,1个数进行统计,
如果某一位的1个数大于0个数,就对这一位$\oplus$1, 因为1 $\oplus$ 1 = 0;

其实可以反向理解一波,为什么要在这一位上异或一个1? 因为正是因为x这个数上有这个0/1才使得数组$a$不符合上述性质

Code

void solve()
{
    memset(cnt, 0,sizeof cnt);

    int l, r;   cin >> l >> r;
    for (int i = l; i <= r; i++)
    {
        int x;  cin >> x;
        for (int j = 0; j < 30; j++)    
            cnt[j][x >> j & 1]++;  // 统计每一个数的每一位的0/1个数
    }

    int res = 0;
    for (int i = 0; i < 30; i++)
        if (cnt[i][1] > cnt[i][0])  res += 1 << i; 

    cout << res << endl;
}

TIPS: 如果WA了,那一定是精度问题,本人代码头文件不同

上一篇 下一篇