• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
关于mutiset的应用的几个题

关于mutiset的应用的几个题

今天kk同学给了我两个题,都是是multiset的题。它的一些性质和应用还是很重要哒!

G - Minimum Xor Pair Query

题意:你可以进行以下操作

  1. 加入一个数
  2. 删除一个数
  3. 输出任意两个数异或最小值

思路:首先我们要知道一个性质(重要!):两个数差值越小,异或值也越小。

那么我们要求异或最小值,那么答案一定在排序后相邻两个数中产生。

为了便于写,防止在set里面找不到的情况出现,我们手动插入下界和上界。

考虑我们的操作:

对于加入一个数,我们的答案有什么影响呢?

假设我们插入的值是\(x\)。

假设我们的情况是:\(l\) \(x\) \(r\)

首先如果\(l\)和\(r\)都存在,那么显然此时\(l\)和\(r\)的异或值不可能成为答案,我们把它删去

然后在答案里面加入x^l和 x^r(前提是l,r存在)

void add(ll x)
{
    s.insert(x);
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.erase(res.find((*it1) ^ (*it2)));
    if(*it1 != -1)
        res.insert(x ^ (*it1));
    if(*it2 != up)
        res.insert(x ^ (*it2));
    //cout<<x<<'\n';
}

对于删一个数的情况呢?

那就和加一个数的操作反过来就行了。

void del(ll x)
{
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.insert((*it1) ^ (*it2));
    if(*it1 != -1)
        res.erase(res.find((*it1) ^ x));
    if(*it2 != up)
        res.erase(res.find(x ^ (*it2)));
    s.erase(s.find(x));
}

下面是AC代码:

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const ll up = 1ll<<60;
multiset<ll>s,res;
void add(ll x)
{
    s.insert(x);
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.erase(res.find((*it1) ^ (*it2)));
    if(*it1 != -1)
        res.insert(x ^ (*it1));
    if(*it2 != up)
        res.insert(x ^ (*it2));
    //cout<<x<<'\n';
}
void del(ll x)
{
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.insert((*it1) ^ (*it2));
    if(*it1 != -1)
        res.erase(res.find((*it1) ^ x));
    if(*it2 != up)
        res.erase(res.find(x ^ (*it2)));
    s.erase(s.find(x));
}
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int q;
    cin>>q;
    s.insert(-1),s.insert(up);
    while(q--)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x;
            cin>>x;
            add(x);
        }
        else if(op==2)
        {
            int x;
            cin>>x;
            del(x);
        }
        else
        {
            cout<<*res.begin()<<"\n";
        }
    }


    return 0;
}

G. The Great Equalizer

题意:

一次运行包含以下操作:

  1. 对当前数组排序(不降序),然后删除重复的元素
  2. 如果当前数组大小是1,那么停止运行,输出运行结果
  3. 对于数组元素加上一个等差数列\(\{n,n-1,n-2,...,1\}\)其中\(n\)是当前数组长度
  4. 返回第一步

给你\(q\)个询问,每次给你\(i\) \(x\),表示把\(a[i]\)赋值为\(x\)。然后运行上述。

思路:

第一次写的时候TLE了,因为我这样写会每次重新计算答案,复杂度很高。下面是错误代码QAQ

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

int t,n,q,a[N],b[N],d[N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i = 1;i <= n; i++)
            cin>>a[i];

        cin>>q;
        while(q--)
        {
            int i,x;
            cin>>i>>x;
            a[i] = x;
            multiset<int>s;
            for(int i = 1;i <= n; i++)
                b[i] = a[i];
            sort(b+1,b+1+n);
            for(int i = 1;i < n; i++)
                d[i] = b[i+1]-b[i];
            // cout<<"b[i] = ";
            // for(int i = 1;i <= n; i++)
            //     cout<<b[i]<<" ";
            // cout<<"\n";
            // cout<<"d[i] = ";
            // for(int i = 1;i < n; i++)
            //     cout<<d[i]<<" ";
            // cout<<"\n";
       
            for(int i = 1;i < n; i++)
                s.insert(d[i]);
           
            ll ans = b[1];
            int ff = 0;
            while(!s.empty())
            {
                int sz = s.size();
                // cout<<"sz = "<<sz<<" fi = "<<*s.begin()<<"\n";
                // cout<<"ff = "<<ff<<"\n";
                ans += (sz+1)*(*s.begin()-ff);
                int tmp = *s.begin();

                ff = ff+tmp-ff;
                s.erase(tmp);    
            }
            cout<<ans<<" ";
        }
        cout<<"\n";
    }
/*
1
3
2 4 8
3
1 6
2 10
3 1
*/

    return 0;
}

那么正解是什么呢?

通过找规律,我们可以发现,最后的答案是数组最大的元素值+排序后数组中两个数差的最大值。

因为,每次操作,排序后的数组,相邻两个数的差值-1,数组中最大值+1,那么答案就是数组最大的元素值+排序后数组中两个数差的最大值。和上一题类似,不过改成了维护两个数差的最大值。

注意:其实改一个数可以想象成把原本的数删了加入一个新的数。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

int t,n,q,a[N];
multiset<ll>s,res;
const ll up = 1ll<<60;
void add(ll x)
{
    s.insert(x);
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.erase(res.find((*it2) - (*it1)));
    if(*it1 != -1)
        res.insert(x - (*it1));
    if(*it2 != up)
        res.insert((*it2) - x);
    //cout<<x<<'\n';
}

void del(ll x)
{
    multiset<ll>::iterator it1 = s.find(x), it2 = s.find(x);
    it1--, it2++;
    if(*it1 != -1 && *it2 != up) 
        res.insert((*it2) - (*it1));
    if(*it1 != -1)
        res.erase(res.find(x - (*it1)));
    if(*it2 != up)
        res.erase(res.find((*it2) - x));
    s.erase(s.find(x));
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>t;
    while(t--)
    {
        cin>>n;
        res.clear(),s.clear();
        s.insert(-1),s.insert(up);
        for(int i = 1;i <= n; i++)
            cin>>a[i],add(a[i]);
        cin>>q;
        while(q--)
        {
            int i,x;
            cin>>i>>x;
            del(a[i]);
            a[i] = x;
            add(a[i]);
            if(n==1){
                cout<<a[n]<<"\n";
                continue;
            }
            cout<<*(--(--s.end()))+*(--res.end())<<" ";
        }
        cout<<"\n";
    }
    return 0;
}

posted on 2023-09-27 22:44  nannandbk  阅读(58)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3