首页 > 试题广场 >

数组同构

[编程题]数组同构
  • 热度指数:628 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}定义变换函数:
\hspace{23pt}\bullet\,将一个正整数 x 用其二进制表示中 1 的个数替换,记作 g(x)(即 \operatorname{popcount});
\hspace{15pt}给定两个长度均为 n 的正整数数组 AB
\hspace{15pt}你可以对 AB 中的任意元素反复执行以下操作,每次操作计数 1
\hspace{23pt}\bullet\,将该元素 x 替换为 g(x)
\hspace{15pt}当且仅当存在置换 \pi,使得对所有 1\leqq i\leqq n 都有 A_i = B_{\pi(i)},也就是两个数组都排序后完全相同,我们称 AB 同构。请计算使 AB 同构所需的最少操作次数。
\hspace{15pt}可以证明题目一定有解。

输入描述:
\hspace{15pt}第一行输入一个整数 t\ \left(1\leqq t\leqq 10^{4}\right),表示测试用例数; 
\hspace{15pt}每个测试用例输入格式如下:
\hspace{23pt}\bullet\,第一行输入一个整数 n\ \left(1\leqq n\leqq 2\times 10^{5}\right)
\hspace{23pt}\bullet\,第二行输入 n 个整数 A_1, A_2, \dots, A_n\ \left(1\leqq A_i\leqq10^{18}\right)
\hspace{23pt}\bullet\,第三行输入 n 个整数 B_1, B_2, \dots, B_n\ \left(1\leqq B_i\leqq10^{18}\right)
\hspace{15pt}保证所有测试用例中 \sum n \leqq 2\times 10^{5}


输出描述:
\hspace{15pt}对于每个测试用例,输出一行整数——使 AB 同构的最少操作次数。
示例1

输入

2
3
4 1 2
2 2 1
3
7 3 5
3 3 5

输出

2
1

说明

\hspace{23pt}\bullet\,初始时,A=\{4,1,2\},\ B=\{2,2,1\}
\hspace{23pt}\bullet\,A 中元素 4 执行一次变换,得到 g(4)=1,此时 A=\{1,1,2\}
\hspace{23pt}\bullet\,B 中一个元素 2 执行一次变换,得到 g(2)=1,此时 B=\{1,2,1\}
\hspace{23pt}\bullet\,此时两数组的元素可以一一匹配,故最少操作数为 2

\hspace{15pt}在第二个测试用例中:仅需将 A 中的 7 变换为 g(7)=3,得到 A=\{3,3,5\},与 B 相同,操作数为 1
#include <bits/stdc++.h>
using namespace std;

unsigned long long popcount(unsigned long long x) { return __builtin_popcount(x); }

unsigned long long maxx=0;

unsigned long long process(unordered_map<unsigned long long,unsigned long long>& a, unordered_map<unsigned long long,unsigned long long>& b, unsigned long long thread){
    // 匹配减少
    for(auto it = a.begin(); it != a.end(); ) {
        unsigned long long num = it->first, cnt = it->second;
        if(b.count(num)){
            unsigned long long minus = min(cnt, b[num]);
            it->second -= minus;
            b[num] -= minus;
            if(b[num] == 0) b.erase(num);
            if(it->second == 0) it = a.erase(it);  // 使用迭代器
            else ++it;
        } else {
            ++it;
        }
    }

    unsigned long long res = 0;

    // popcount操作
    for(auto it = a.begin(); it != a.end(); ) {
        unsigned long long num = it->first, cnt = it->second;
        if(num >= thread){
            res += cnt;
            it = a.erase(it);  // 使用迭代器
            unsigned long long newNum = popcount(num);
            a[newNum] += cnt;  // 可以直接累加
            maxx=max(maxx,newNum);
        } else {
            ++it;
            maxx=max(maxx,num);
        }
    }

    for(auto it = b.begin(); it != b.end(); ) {
        unsigned long long num = it->first, cnt = it->second;
        if(num >= thread){
            res += cnt;
            it = b.erase(it);  // 使用迭代器
            unsigned long long newNum = popcount(num);
            b[newNum] += cnt;
            maxx=max(maxx,newNum);
        } else {
            ++it;
            maxx=max(maxx,num);
        }
    }

    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    unsigned long long t;
    cin >> t;
    while(t--) {
        unsigned long long n;
        cin >> n;
        unordered_map<unsigned long long,unsigned long long> cntA,cntB;
        
        maxx=-1;
        for(unsigned long long i=0;i<n;++i){
            unsigned long long temp;
            cin>>temp;
            if(cntA.count(temp)){
                cntA[temp]++;
            }else{
                cntA[temp]=1;
            }
            maxx=max(maxx,temp);
        }
        for(unsigned long long i=0;i<n;++i){
            unsigned long long temp;
            cin>>temp;
            if(cntB.count(temp)){
                cntB[temp]++;
            }else{
                cntB[temp]=1;
            }
            maxx=max(maxx,temp);
        }

        unsigned long long ops = 0;

        // 去重
        vector<unsigned long long> threads={21,5,3,2,0};
        for(unsigned long long i:threads){
            if(maxx>=i){
                maxx=0;
                ops += process(cntA,cntB,i);
            }else{
                continue;
            }
        }

        cout << ops << "\n";
    }
}

求路过大神看一看,上述代码可以通过4/20用例,错的情况比预期高。
我的思路是,逐层处理,例如thread=5,对大于等于5的数,做一次popcount(结果会是1/2/3/4),小于5的数就先不管。这样一来,所有的数都来到了[1,4],下一轮开始时,先去重。再对剩下的,按新阈值3处理。
编辑于 2025-09-16 21:25:25 回复(0)