算法复健::ID_1

D. Another Problem About Dividing Numbers

题目传送门 : https://codeforces.com/problemset/problem/1538/D

复健后第一道成功 AC 的题, 好像还是 [普及+/提高]

我们先分析特殊情况, 如果 $c = 1$ 的时候, 那么如果可以, 那么要么 $a = kb$ 成立或者 $b = ka$ 成立

注意, $c = 1$ 的时候 $a, b$ 不能相等, 即 $k \ne 1$, 因为题目要求 $c > 1$

所以如果 $c = 1$ ,如果有一方是另一方的倍数, 且 $a,b$ 不相等那么就可以, 否则一定不行

接下来分析 $c \ne 1$ 的情况, 我们知道最少需要两步就一定可以让两个数相等

那就是 $a$ 除 $b$, $b$ 除 $a$

我们就考虑如何能将 $a$ 和 $b$ 拆分成 $k$ 个数相乘就可以得到最大的拆分次数了

$k$ 的最大值可以用分解质因数获得, 直接分解会超时, 所以我们将质数打表即可

#include<bits/stdc++.h>
using namespace std;
typedef long long int lli;

vector<lli>info;
bitset<lli(1e8 + 10)> Check;
void Euler_sieve(lli Limit){
    for(lli temp = 2 ; temp <= Limit ; temp++){
        if(Check[temp] == 0) info.push_back(temp);
        for(lli temp2 = 0 ; temp*info[temp2] <= Limit ; temp2++){
            Check[temp*info[temp2]] = 1;
            if(temp % info[temp2] == 0) break;
        }
    }
}

void solve() {
    lli a, b, c;
    cin >> a >> b >> c;
    if(c == 1) {
        if(max(a, b) % min(a, b) == 0 && a != b) cout << "YES\n";
        else cout << "NO\n";
        return;
    }
    lli count1 = 0, count2 = 0;
    for(int i = 0 ; i < info.size() && info[i] * info[i] <= a ; i ++) {
        while (a % (info[i]) == 0) {
            count2 ++; a /= info[i];
        }
    }
    if(a != 1) count2 ++;
    for(int i = 0 ; i < info.size() && info[i] * info[i] <= b ; i ++) {
        while (b % info[i] == 0) {
            count1 ++; b /= info[i];
        }
    }
    if(b != 1) count1 ++;
    if(c > count1 + count2) {
        cout << "NO\n"; return;
    }
    if(c == 1 && (count1 && count2)) {
        cout << "NO\n"; return;
    }
    cout << "YES\n";
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    lli ask; cin >> ask;
    Euler_sieve(1e5);
    while (ask --)
        solve();
    return 0;
}

D. Sum of XOR Functions

题目链接: https://codeforces.com/problemset/problem/1879/D

第二道过的题目, 好像也是 [普及+/提高], 感觉写起来挺 caodan 的

思路倒是不是很困难的想出来, 但是写起来巨麻烦和恶心, 一直在理清自己的思路

但这好像有套路 (感觉可以回去看看题解总结一下) : https://www.luogu.com.cn/problem/solution/CF1879D

具体的做法就是 :

每一位算贡献, 计算这一位在答案中总共被加上了多少次

我们重点关注从 $[a_0, a_1, a_2] \Rightarrow [a_0, a_1, a_2, a_3]$ 增加的值

假设我们有一个序列 $[1, 0, 1, 0]$, 我们用递推的思维简单看一下:

$[1]$ : 答案就是 $1$

$[1,0]$ : 相比于上一个序列增加了 $1\times0 + 2\times1$

$[1,0,1]$ : 相比于上一个序列增加了 $1\times1 + 2\times1 + 3\times0$

$[1,0,1,0]$ : 相比于上一个序列增加了 $1\times0 + 2\times1 + 3\times1 + 4\times0$

到这里规律其实就很明显了, [不想说了, 自己看….]

所以难写的点在于怎么滤清需要什么变量来维护这些变化

直接上代码吧:

#include<bits/stdc++.h>
using namespace std;
typedef long long int lli;
lli len;
const int N = 5e5;
const int P = 998244353;

lli arr[N];
lli bit[33][N];

lli queryNum(int pos) {
    lli ans = bit[pos][1] ? 1 : 0;
    lli last_add = bit[pos][1] ? 1 : 0;
    lli one_num = bit[pos][1] ? 1 : 0;
    lli sum = 1;
    for(int i = 2 ; i <= len; sum += i, i ++, sum %= P) {
        lli add = 0;
        if(bit[pos][i] == 0) {
            add = last_add + one_num;
            add %= P;
        }else {
            lli tem = (sum - last_add + P) % P;
            tem %= P;
            one_num = (i - one_num - 1 + P) % P;
            add = (tem + one_num) % P;
            one_num ++; add ++;
            one_num %= P; add %= P;
        }
        ans += add;
        ans %= P;
        last_add = add;
    }
    return ans;
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> len;
    for(int i = 1 ; i <= len ; i ++) cin >> arr[i];
    for(int i = 0 ; i <= 32 ; i ++) {
        for(int j = 1 ; j <= len ; j ++)
            bit[i][j] = arr[j] >> i & 1;
    }
    lli ans = 0;
    for(int i = 0 ; i <= 32 ; i ++) {
        lli num = queryNum(i); num %= P;
        ans += (num * ((1LL << i) % P)) % P;
        ans %= P;
    }

    cout << ans << "\n";
    return 0;
}