再读《挑战程序设计竞赛》——初出茅庐(4)

加工并存储数据的数据结构

重点

  1. 平衡树(使用unordered_map,multiset,multimap),优先队列(手写堆)
  2. 并查集

平衡树
用平衡树维护的数,集合,区间,会有一个很好的单调性,可以二分。一般用set维护平衡树(insert,find,erase,count)。用map维护键值对。

//声明int为键,const char*为值
map<int,const char*> m;
//插入元素 
m.insert(make_pair(1,"one"));
n.insert(make_pair(10,"ten"));
m [100]="hundred";
//查找元素
map<int,const char*>::iterator it;
it=m.find(1);
puts(it->second);
//没有找到的情况 
it=m.find(2);
if(it==m.end) puts("not found");
else puts(it->second);

puts(m[10]);

//删除元素
m.erase(10);
//遍历一遍元素
for(it=m.begin();it!=m.end();++it)
{
	cout<<it.first<<" "<<it.second<<endl;
 } 

unordered_map用于哈希

看这个

优先队列
用priority_queue的话不能删除某个任意值,因此一般如果要删除,就需要手写。

#include<iostream>
using namespace std;
const int N=2e5+10;
int h[N],pos[N],pos2[N];
int idx=0;
int size=0;
void down(int x)
{
    int t=x;
    if(x*2<=size&&h[x*2]<h[t])  t=2*x;
    if(x*2+1<=size&&h[x*2+1]<h[t]) t=2*x+1;
    if(t!=x)
    {
        swap(h[t],h[x]);
        swap(pos2[t],pos2[x]);
        swap(pos[pos2[t]],pos[pos2[x]]);
        down(t);
    }
}
void up(int x)
{
    if(x/2>0&&h[x/2]>h[x]) 
    {  
        swap(h[x/2],h[x]);
        swap(pos2[x/2],pos2[x]);
        swap(pos[pos2[x/2]],pos[pos2[x]]);
        up(x>>1);
    }
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        string op;
        cin>>op;
        if(op=="I")
        {
            int x;
            cin>>x;
            idx++;
            h[++size]=x;
            pos[idx]=size;
            pos2[size]=idx;
            up(size);
        }
        else if(op=="PM")
        {
            cout<<h[1]<<endl;
        }
        else if(op=="DM")
        {
            swap(h[1],h[size]);
            swap(pos2[1],pos2[size]);
            swap(pos[pos2[1]],pos[pos2[size]]);
            size--;
            down(1);
        }
        else if(op=="D")
        {
            int k;
            cin>>k;
            int u=pos[k];
            swap(h[u],h[size]);
            swap(pos2[u],pos2[size]);
            swap(pos[pos2[size]],pos[pos2[u]]);
            size--;
            up(u);
            down(u);
        }
        else if(op=="C")
        {
            int k,x;
            cin>>k>>x;
            h[pos[k]]=x;
            up(pos[k]);
            down(pos[k]);
           
        }
    }
    /*cout<<endl;
    for(int i=1;i<=size;i++)
    {
        cout<<h[i]<<" ";
    }*/
    return 0;
}

并查集
简单的合并集合(模板题)

#include<iostream>
using namespace std;
int p[100010];
int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
    }
    int a,b;
    string op;
    while(m--)
    {
        cin>>op>>a>>b;
        if(op=="M")
        {
            p[find(b)]=find(a);
        }
        else
        {
            if(find(b)==find(a))
            {
                cout<<"Yes"<<endl;
            }
            else
            {
                cout<<"No"<<endl;
            }
        }
    }
    return 0;
}

另外kruskal,图的联通块问题都是以并查集为基础的算法。
食物链
建立三个并查集,把每一个物种归类。

//用x,x+N,x+2*N代表xA,xB,xC
if(t==1)
{
	if(same(x,y+N)||same(x,y+N*2)) 
	{
		ans++;
	}
	else
	{
		unite(x,y);
		unite(x+N,y+N);
		unite(x+N*2,y+N*2); 
	}
}
else
{
	if(same(x,y)||same(x,y+2*N))
	{
		ans++;
	}
	else
	{
		unite(x,y+N);
		unite(x+N,y+2*N);
		unite(x+2*N,y);
	}
}

等式与不等式
hash+并查集

#include<iostream>
using namespace std;
typedef long long ll;
int p[514748];
int mod=514747;
int a[100001];
int b[100001];


int find(int a)
{
	return p[a]==a?a:(p[a]=find(p[a]));
} 
void merge(int x,int y)
{
    p[find(x)]=find(y);
}
int main()
{
    int n,t;
    cin>>t;
    for(int q=1;q<=t;q++)
    {
        int flag=1;
        int cnt=0;
        for(int i=0;i<=514747;i++)
            p[i]=i;        
        cin>>n;
        for(int k=1;k<=n;k++)
        {
            bool e;
            ll i,j;
            cin>>i>>j>>e;
            if(e)
            {
                merge(i%mod,j%mod);
            }
            else
            {
                a[++cnt]=i%mod;
                b[cnt]=j%mod;
            }
        }
        for(int j=1;j<=cnt;j++)
        {
            if(find(a[j])==find(b[j]))
            {
                flag=0;
                cout<<"NO"<<endl;
                break;
            }
        }
        if(flag)
        cout<<"YES"<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值