数据结构总结
-
堆(优先队列)
开一个大根堆,一个小根堆,保证大根堆的元素个数为查询的i-1,输出小根堆的堆顶即可。如果插入的数a[j]比大根堆堆顶小,则把大根堆堆顶放到小根堆,a[j]放到大根堆里,保证大根堆里的最大值小于小根堆里的最小值。查询时把小根堆的堆顶放到大根堆里,使大根堆元素个数+1。
code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=200010;
priority_queue<int>qmax;
priority_queue<int,vector<int>,greater<int> >qmin;
int m,n;
int a[N],u[N];
int main()
{
scanf("%d%d",&m,&n);
int j=0;
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&u[i]);
for(int i=1;i<=n;i++)
{
while(j<u[i])
{
j++;
if(!qmax.size()||a[j]>qmax.top())
{
qmin.push(a[j]);
}
if(qmax.size()&&a[j]<qmax.top())
{
qmin.push(qmax.top());
qmax.pop();qmax.push(a[j]);
}
}printf("%d
",qmin.top());
qmax.push(qmin.top());qmin.pop();
}
}
-
线段树
直接做会觉得无从下手,我们考虑二分一个排名k,把比k小的数变成0,>=k的变成1,这样判断查找的pos值是0还是1,如果是1,说明二分的排名小了,否则说明排名大了,check用线段树维护区间1的个数,并支持覆盖(染色)和查询操作。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define lch (now<<1)
#define rch (now<<1|1)
#define smid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
struct node
{
int l,r,cover,sum;
}sgt[N<<2];
int n,m,pos;
int b[N];
struct Q
{
int cid,l,r;
}q[N];
void pushup(int now)
{
sgt[now].sum=sgt[lch].sum+sgt[rch].sum;
}
void bt(int now,int l,int r,int x)
{
sgt[now].cover=-1;
if(l==r)
{
sgt[now].sum=b[l]>=x;return;
}
bt(lch,l,smid,x);
bt(rch,smid+1,r,x);
pushup(now);
}
void pushdown(int now,int l,int r)
{
if(sgt[now].cover==-1)return;
sgt[lch].cover=sgt[rch].cover=sgt[now].cover;
if(sgt[now].cover==1)sgt[lch].sum=(smid-l+1),sgt[rch].sum=(r-smid);
else sgt[lch].sum=sgt[rch].sum=0;
sgt[now].cover=-1;
}
int query(int now,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return sgt[now].sum;
if(x>r||y<l)return 0;
pushdown(now,l,r);
int res=0;
res+=query(lch,l,smid,x,y);
res+=query(rch,smid+1,r,x,y);
return res;
}
void modify(int now,int l,int r,int x,int y,int val)
{
if(x<=l&&r<=y)
{
if(val==1)sgt[now].sum=(r-l+1),sgt[now].cover=1;
else sgt[now].sum=0,sgt[now].cover=0;
return;
}
if(x>r||y<l)return;
pushdown(now,l,r);
modify(lch,l,smid,x,y,val);
modify(rch,smid+1,r,x,y,val);
pushup(now);
}
bool check(int mid)
{
bt(1,1,n,mid);
for(int i=1;i<=m;i++)
{
int res=query(1,1,n,q[i].l,q[i].r);
if(q[i].cid==0)
{
modify(1,1,n,q[i].r-res+1,q[i].r,1);
modify(1,1,n,q[i].l,q[i].r-res,0);
}
else
{
modify(1,1,n,q[i].l,q[i].l+res-1,1);
modify(1,1,n,q[i].l+res,q[i].r,0);
}
}
if(query(1,1,n,pos,pos)==1)return 1;
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&q[i].cid,&q[i].l,&q[i].r);
scanf("%d",&pos);
int L=0,R=N;
while(L<R-1)
{
int mid=((L+R)>>1);
if(check(mid))L=mid;
else R=mid;
}
printf("%d",L);
}
-
树状数组(Binary Indexed Tree)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define go(i,a,b) for(int i=a;i<=b;i++) #define lowbit(x) (x&-x) #define LL long long using namespace std; const int N=20010; struct COW { int v,x; }c[N]; int n; LL tot,ans; LL bit1[N],bit2[N]; bool cmp(COW a,COW b) { return a.v<b.v; } int query_bit1(int pos) { int res=0; while(pos) { res+=bit1[pos]; pos-=lowbit(pos); }return res; } int query_bit2(int pos) { int res=0; while(pos) { res+=bit2[pos]; pos-=lowbit(pos); }return res; } void add_bit1(int pos,int val) { while(pos<=N) { bit1[pos]+=val; pos+=lowbit(pos); } } void add_bit2(int pos,int val) { while(pos<=N) { bit2[pos]+=val; pos+=lowbit(pos); } } int main() { scanf("%d",&n); go(i,1,n) scanf("%d%d",&c[i].v,&c[i].x); sort(c+1,c+1+n,cmp);//按v排序,这样可以不用比max for(int i=1;i<=n;i++)//bit1维护比x[i]小的个数,bit2维护比x[i]小的x[j]的和 { LL num=query_bit1(c[i].x);// LL sum=query_bit2(c[i].x); ans+=c[i].v*(num*c[i].x-sum); ans+=c[i].v*((tot-sum)-(i-1-num)*c[i].x); tot+=c[i].x; add_bit1(c[i].x,1); add_bit2(c[i].x,c[i].x); } printf("%lld",ans); }
-
并查集
SP5150 JMFILTER - Junk-Mail Filter
merge和find操作没什么不同。难点在删除操作。
我们给0->n-1的每个点的父亲赋为n->n*2-1,相当于一个虚拟父亲。这样我们在0->n-1元素中合并时,根节点始终在n->n*2-1,我们在从并查集中删除节点时,直接把其父亲赋为n*2->m中的元素,这样和它在同一个集合里的元素根在n->n*2-1中,它可以直接删除。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int M=1e6+10;
int father[2*N+M];
bool vis[2*N+M];
int n,m,tmp;
int find(int x)
{
if(x==father[x])return x;
return father[x]=find(father[x]);
}
int main()
{
int T=0;
while(1)
{
scanf("%d%d",&n,&m);
if(!n&&!m)return 0;
tmp=n<<1;T++;
for(int i=0;i<n;i++)father[i]=i+n;
for(int i=n;i<tmp+m;i++)father[i]=i;
char opt;memset(vis,0,sizeof(vis));
while(m--)
{
cin>>opt;
if(opt=='M')
{
int x,y;scanf("%d%d",&x,&y);
int r1=find(x),r2=find(y);
father[r1]=r2;
}
if(opt=='S')
{
int x;scanf("%d",&x);
father[x]=tmp++;
}
}int ans=0;
for(int i=0;i<n;i++)
{
int r=find(i);if(!vis[r])ans++,vis[r]=1;
}
printf("Case #%d: %d
",T,ans);
}
}