首页 > 试题广场 >

数组同构

[编程题]数组同构
  • 热度指数:876 时间限制: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;
int cntOfOne(long long num) {
    int cnt = 0;
    //需要确保num的二进制不会有64个1
    while (num) {
        if (num & 1) {
            cnt++;
        }
        num = num >> 1;
    }
    return cnt;
}
int main() {
    //所有数 -> [1,63] -> [1,6] -> [1,2]
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;int tt=t;
    while (tt--) {
        int n;
        cin >> n;
        
        vector<long long> num1(n, 0);
        vector<long long> num2(n, 0);
        for (int i = 0; i < n; i++) {
            cin >> num1[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> num2[i];
        }
        

        int ans = 0;
        int cnt[64]; memset(cnt, 0, sizeof(cnt));

        unordered_map<long long, int> mp;
        for (int i = 0; i < n; i++) {
            mp[num1[i]]++;
            mp[num2[i]]--;
        }
        for (auto num : mp) {
            if (num.second > 0) {
                if (num.first > 63) {
                    ans+=num.second;
                    cnt[cntOfOne(num.first)]+=num.second;
                }
                else {
                    cnt[num.first]+= num.second;
                }
            }
            else if(num.second < 0){
                if (num.first > 63) {
                    ans+=(-num.second);
                    cnt[cntOfOne(num.first)]+= num.second;
                }
                else {
                    cnt[num.first]+= num.second;
                }
            }
        }
       //if(t==11){ for(auto n:cnt)cout<<n<<" ";cout<<endl;}
        int cnt2[7]; memset(cnt2, 0, sizeof(cnt2));

        for (int i = 1; i < 64; i++) {
            if (cnt[i] > 0) {
                if (i > 6) {
                    cnt2[cntOfOne(i)]+=cnt[i];
                     ans+=cnt[i];
                }
                else {
                    cnt2[i]+=cnt[i];
                }
            }
            else if (cnt[i] < 0) {
                if (i > 6) {
                    cnt2[cntOfOne(i)]+=cnt[i];
                    ans+=(-cnt[i]);
                }
                else {
                    cnt2[i]+=cnt[i];
                }
            }
        }
        //for(auto n:cnt2)cout<<n<<" ";cout<<endl;
        int cnt3[3] = { 0,0,0 };
        for (int i = 1; i < 7; i++) {
            if (cnt2[i] > 0) {
                if (i > 2) {
                    ans += cnt2[i];
                    cnt3[cntOfOne(i)] += cnt2[i];
                }
                else {
                    cnt3[i] += cnt2[i];
                }
            }
            else if (cnt2[i] < 0) {
                if (i > 2) {
                    ans += (-cnt2[i]);
                    cnt3[cntOfOne(i)] += cnt2[i];
                }
                else {
                    cnt3[i] += cnt2[i];
                }
            }
        }
        //for(auto n:cnt3)cout<<n<<" ";cout<<endl;
        ans += abs(cnt3[1]);
       
        cout << ans << endl;

    }
}
// 64 位输出请用 printf("%lld")

发表于 2025-09-19 22:06:55 回复(0)
#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)