[AcWing],Trie树,并查集,堆

问题来源 : ACWing
https://www.acwing.com/blog/content/277/

Trie字符串统计

在这里插入图片描述

#include <iostream>
#include <string>

using namespace std;

const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;

void insert(const string& str) {
    int p = 0;
    for(int i = 0; i < str.size(); i++) {
        int pos = str[i] - 'a';
        if(!son[p][pos]) son[p][pos] = ++idx;
        p = son[p][pos];
    }
    
    cnt[p] ++;
}

int query(const string& str) {
    int p = 0;
    for(int i = 0; i < str.size(); i++) {
        int pos = str[i] - 'a';
        if(!son[p][pos]) return 0;
        p = son[p][pos];
    }
    return cnt[p];
}

int main() {
    int n;
    cin >> n;
    
    while(n --) {
        char optr;
        string str;
        cin >> optr >> str;
        if(optr == 'I') insert(str);
        else if(optr == 'Q') cout << query(str) << endl;
    }
    
    return 0;
}

最大异或对

在这里插入图片描述

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100010;
const int M = N * 31;

int num[N];
int son[M][2];
int id;

//插入数据到字典树中,一个根的左节点是1,右节点是0
void insert(int num)
{
    int p = 0;
    // for(int i = 30; i >= 0; i--)
    for(int i = 30; ~i; i--)
    {
        //获取第i位的数字 0 或者 1
        // int& s = son[p][num>>i & 1];
        // if(!s) s = ++id; //创建一个新结点
        // p = s;
        int tmp = num>>i&1;
        if(!son[p][tmp]) son[p][tmp] = ++id;
        p = son[p][tmp];
    }
}

//找到与这个数异或后最大的结果
int Find(int num)
{
    int ret = 0;
    int p = 0;
    for(int i = 30; ~i; i--)
    {
        int s = num>>i & 1;
        //异或的话,同则为0,异则为1
        if(son[p][!s])//如果存在该位置不同的数,则走这个分支
        {
            ret += 1 << i;
            p = son[p][!s];
        }
        else
            p = son[p][s];
    }
    
    return ret;
}

int main()
{
    int n;
    cin>>n;
    for(int i = 0; i < n; i++)
    {
        cin>>num[i];
        insert(num[i]);//插入该数据到字典树种
    }
    
    int ans = 0;
    //找异或的最大值
    for(int i = 0; i < n; i++)
        ans = max(ans,Find(num[i]));
    
    cout<<ans<<endl;
    return 0;
}

// //暴力法
// int main()
// {
//     int n;
//     cin>>n;
//     for(int i = 0; i < n; i++)
//         cin>>num[i];
//     int ans = 0;
//     for(int i = 0; i < n; i++)
//         for(int j = 0; j < n; j++)
//             ans = max(ans,num[i]^num[j]);
//     cout<<ans<<endl;
//     return 0;
// }

合并集合

在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int n,m;
int f[N];

int find(int x) {
    if(f[x] != x) f[x] = find(f[x]);
    return f[x];
}

int main() {
    cin >> n >> m;
    for(int i = 0; i <= n; i++) f[i] = i;
    
    while(m --) {
        char oper[2];
        int a,b;
        scanf("%s%d%d",oper,&a,&b);
        
        if(oper[0] == 'M') f[find(a)] = find(b);
        else if(oper[0] == 'Q') {
            if(find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }

    return 0;
}

连通块中点的数量

在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int f[N],s[N];
int n,m;

int find(const int x) {
    if(f[x] != x) f[x] = find(f[x]);
    return f[x];
}

bool query(const int x,const int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx == fy) return false;
    f[fx] = fy;
    s[fy] += s[fx];
    return true;
}

int sum(const int x) {
    int fx = find(x);
    return s[fx];
}

int main() {
    cin >> n >> m;
    for(int i = 0; i <= n; i++) f[i] = i, s[i] = 1;
    
    while(m --) {
        string oper;
        cin >> oper;
        
        int a,b;
        if(oper == "C") {
            cin >> a >> b;
            query(a,b);
        } else if(oper == "Q1"){
            cin >> a >> b;
            if(f[find(a)] == f[find(b)]) cout << "Yes" << endl;
            else cout << "No" << endl;
        } else if(oper == "Q2") {
            cin >> a;
            cout << s[find(a)] << endl;
        }
    }
    
    return 0;
}

堆排序

在这里插入图片描述

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;
int f[N];
int n,m;

bool check(const int le,const int ri) {
    return f[le] > f[ri];
}

// 向下调整取堆顶元素
int pop() {
    int top = f[1];
    swap(f[1],f[n--]);
    
    int parent = 1;
    int child = parent * 2;
    while(child <= n) {
        if(child < n && check(child, child + 1)) child ++;
        if(check(parent,child)) {
            swap(f[parent],f[child]);
        } else {
            break;
        }
        parent = child;
        child = parent * 2;
    }
    
    return top;
}

// 向上调整建堆
void push(const int idx) {
    int child = idx;
    int parent = idx / 2;

    while(parent) {
        if(check(parent,child)) {
            swap(f[parent],f[child]);
        } else {
            break;
        }
        child = parent;
        parent = child / 2;
    }
}

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        scanf("%d",&f[i]);
        push(i);
    }

    while(m --) {
        cout << pop() << " ";
    }
    return 0;
}

模拟堆

在这里插入图片描述
在本题中,需要注意的一个点是,在堆排序(向上调整,向下调整)的基础上,加了一个删除第K个插入的元素,以及替换第K个插入的元素。

  • 而替换,删除中间的一个元素后,必须向上调整一次,并且向下调整一次。因为我们并不确定这个位置新的数据是否比他的父亲节点的数据更小。

第二个点就是,我们如何定位第K个插入的元素的位置,因为堆在向上调整和向下调整的过程中,无可避免会改变原本数据的位置,这个时候就需要两个数组来维护一下

  1. hp[K],在堆中,下标为K的数据是第hp[K]次插入的
  2. ph[K],表示第K次插入的数据在堆中的下标
#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

const int N = 1e5 + 10;
int f[N],idx;
int hp[N],ph[N];    // hp[i] 堆中的第i个点对应的第hp[i]次插入  ph[i] 第i次插入的数在堆中的下标
int n,a,b;

// 大于号,建小堆
bool comper(const int le,const int ri) {
    return f[le] > f[ri];
}

void heap_swap(const int le,const int ri) {
    swap(ph[hp[le]],ph[hp[ri]]);
    swap(hp[le],hp[ri]);
    swap(f[le],f[ri]);
}

// 向上调整
void push(const int x) {
    int ch = x;
    int fa = x / 2;
    while(fa) {
        if(comper(fa,ch)) {
            heap_swap(fa,ch);
        } else {
            break;
        }
        ch = fa;
        fa = ch / 2;
    }
}

// 向下调整
void pop(const int x) {
    int fa = x;
    int ch = fa * 2;
    while(ch <= idx) {
        if(ch < idx && comper(ch,ch + 1)) ch ++;
        if(comper(fa,ch)) {
            heap_swap(fa,ch);
        } else {
            break;
        }
        fa = ch;
        ch = fa * 2;
    }
}

int main() {
    cin >> n;
    int k = 0;
    
    while(n -- ){
        string oper;
        cin >> oper;
        if(oper == "I") {
            cin >> a;
            ++idx;
            ++k;
            f[idx] = a;
            ph[k] = idx;
            hp[idx] = k;
            push(idx);
        } else if(oper == "PM") {
            cout << f[1] << endl;
        } else if(oper == "DM") {
            heap_swap(1,idx);
            idx--;
            pop(1);
        } else if(oper == "D") {
            cin >> a;
            a = ph[a];
            heap_swap(a,idx);
            idx--;
            pop(a),push(a);
        } else if(oper == "C") {
            cin >> a >> b;
            a = ph[a];
            f[a] = b;
            push(a),pop(a);
        }
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值