堆呢就是一棵树完全二叉树。。。
小根堆的话,根节点就是最小值
维护堆只有两个操作
up(k)
down(k)
cnt是堆的大小
建堆的话只需要把前n/2的数down下来就ok
复杂度是小于O(n) 的
#include<iostream>
using namespace std;
const int N=1e5+10;
int h[N],cnt;
void down (int k){
int u=k;
if(u*2<=cnt&&h[u*2]<h[k])k=u*2;
if(u*2+1<=cnt&&h[u*2+1]<h[k])k=u*2+1;
if(k!=u){
swap(h[k],h[u]);
down(k);
}
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&h[i]);
cnt=n; //不要忘记给cnt赋值
for(int i=n/2;i;i--)down(i); //建立小根堆
while(k--){
printf("%d ",h[1]);
h[1]=h[cnt--];
down(1);
}
return 0;
}
模拟堆
和priority_queue不同的是,这个手写的堆可以删除元素
实现的话主要是hp和ph数组,意义是heap指向point,point指向heap
双向实现
然后m代表第m个插入的数
一直维护堆的cnt,即大小
以及维护插入顺序
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10;
int h[N],cnt,hp[N],ph[N];//hp是heap指向point
void heap_swap(int a,int b){
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
void down(int k){
int u=k;
if(u*2<=cnt&&h[u*2]<h[k])k=u*2;
if(u*2+1<=cnt&&h[u*2+1]<h[k])k=u*2+1;
if(u!=k){
heap_swap(k,u);
down(k);
}
}
void up(int k){
while(k/2&&h[k/2]>h[k]){
heap_swap(k,k/2); //heap_swap用的是下标
k>>=1;
}
}
int main(){
int n,m=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
int k,x;
char op[4];
scanf("%s",op);
if(!strcmp(op,"I")){
scanf("%d",&x);
m++;
cnt++;
h[cnt]=x;
hp[cnt]=m;
ph[m]=cnt;
up(cnt);
}else if(!strcmp(op,"PM")){
printf("%d\n",h[1]);
}else if(!strcmp(op,"DM")){
heap_swap(1,cnt--);
down(1);
}else if(!strcmp(op,"D")){
scanf("%d",&k);
k=ph[k];
heap_swap(k,cnt--);
up(k);
down(k);
}else {
scanf("%d%d",&k,&x);
k=ph[k];
h[k]=x;
up(k);
down(k);
}
}
return 0;
}
关于字典树(trie树)
传送门
维护一颗字典树,和节点的cnt数组
总是会犯char str[N],son[N][30],cnt[N],idx;的傻逼错误
#include<iostream>
using namespace std;
const int N=1e5+10;
char str[N]; //别定义错了。。。
int son[N][30],cnt[N],idx;
void insert(){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u])son[p][u]=++idx; //
p=son[p][u];
}
cnt[p]++;
}
int query(){
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u])return 0;
p=son[p][u];
}
return cnt[p];
}
int main(){
int n;
scanf("%d",&n);
while(n--){
char op[5];
scanf("%s%s",op,str);
if(*op=='I'){
insert();
}else {
printf("%d\n",query());
}
}
return 0;
}
异或字典树
传送门
很好的字典树题目
对于每个数只需要查30次就可以找到最优解
注意节点数要是1e5*30
#include<iostream>
using namespace std;
const int N=3e6+10; //
int son[N][2],idx,q[N],ans=0;
void insert(int x){
int p=0;
for(int i=30;i>=0;i--){
int &s=son[p][x>>i&1];
if(!s)s=++idx;
p=s;
}
}
int query(int x){
int p=0,res=0;
for(int i=30;i>=0;i--){
int s=(x>>i)&1;
if(son[p][!s]){
res+=1<<i; //直接把此位异或结果置为1
p=son[p][!s];
}else {
p=son[p][s];
}
}
return res;
}
int main(){
int n,res=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
int x;
scanf("%d",&q[i]);
insert(q[i]);
}
for(int i=0;i<n;i++){
res=max(query(q[i]),res);
}
printf("%d",res);
return 0;
}
关于并查集
很简单的操作
传送门
#include<iostream>
using namespace std;
const int N=1e5+10;
int ne[N];
int find(int x){
if(ne[x]!=x)ne[x]=find(ne[x]); //递归查找
return ne[x];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)ne[i]=i;//并查集初始化
while(m--){
int a,b;
char op[2];
scanf("%s%d%d",op,&a,&b);
if(*op=='M'){
int aa=find(a),bb=find(b);
if(aa!=bb){
ne[aa]=bb;
}
}else {
int aa=find(a),bb=find(b);
if(aa==bb){
printf("Yes\n");
}else {
printf("No\n");
}
}
}
return 0;
}
这是连通块点的数量
挺简单的。。。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10;
int ne[N],cnt[N];
int find(int x){
if(x!=ne[x])ne[x]=find(ne[x]);
return ne[x];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)ne[i]=i,cnt[i]=1;
while(m--){
int a,b;
char op[5];
scanf("%s",op);
if(!strcmp(op,"C")){
scanf("%d%d",&a,&b);
int aa=find(a),bb=find(b);
if(aa!=bb){
ne[aa]=bb;
cnt[bb]+=cnt[aa];
}
}else if(!strcmp(op,"Q1")){
scanf("%d%d",&a,&b);
int aa=find(a),bb=find(b);
if(aa==bb){
printf("Yes\n");
}else {
printf("No\n");
}
}else {
scanf("%d",&a);
int aa=find(a);
printf("%d\n",cnt[aa]);
}
}
return 0;
}
以前做过的题,现在思路更加清晰了
主要就是存储节点与根节点的距离
根据循环关系来确定是否为谎话
#include<iostream>
using namespace std;
const int N=1e5+10;
int fa[N],d[N];
int find(int x){
if(x!=fa[x]){
int t=find(fa[x]);
d[x]+=d[fa[x]];
fa[x]=t;
}
return fa[x];
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
int res=0;
for(int i=0;i<=n;i++)fa[i]=i;
while(k--){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(x>n||y>n){
res++;
continue;
}
int px=find(x),py=find(y);
if(op==1){
if(px==py&&(d[x]-d[y])%3)res++;
else if(px!=py){
fa[px]=py;
d[px]=(d[y]-d[x]);
}
}else {
if(px==py&&(d[x]-d[y]-1)%3)res++;
else if(px!=py){
fa[px]=py; //吃方比被吃方距离多1
d[px]+=(d[y]-d[x]+1);
}
}
}
printf("%d",res);
return 0;
}