问题来源 : 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个插入的元素的位置,因为堆在向上调整和向下调整的过程中,无可避免会改变原本数据的位置,这个时候就需要两个数组来维护一下
hp[K]
,在堆中,下标为K的数据是第hp[K]
次插入的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;
}