22.4.20~21

1,social distance;2,平衡树treap;3,fhq treap,4,minimal labels


1,social distance

题意:m个椅子围成一圈,有n个人要坐进去,但是每个人有条件限制,要求他左右两边的空椅子必须>ai个,才可以做进去;

不难发现对ai数组排序,从左往右把大的ai安排进去,除了第一个最大ai需要1+ai*2个椅子,其他的都是需要1+ai个,左边的空椅子利用大ai的右边的空椅子;所以只考虑自身和右边;

最后就是对一些特殊情况的判断。

#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++) 
#define rep2(i,a,n) for(register ll i=a;i<=n;i++) 
#define per1(i,n,a) for(register ll i=n;i>a;i--) 
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
#define endl "\n"
#define xiaoshu(a) setprecision(a)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const int N=1e5+10;
int a[N];
void solve()
{
	int n,m;
	cin>>n>>m;
	rep2(i,1,n)cin>>a[i];
	if(n>=m||n>(m+1)/2)
	{
		cout<<"NO\n";
	}
	else
	{
		bool flag=0;
		sort(a+1,a+n+1,greater<int>());
		ll ans=a[1]*2+1;
		rep2(i,2,n)
		{	
			if(ans>m)
			{
				flag=1;
				break;
			}
			if(i<n)
			{
				ans+=1+a[i];	
			}
			else
			{
				ans+=1;	
			}
			if(ans>m)
			{
				flag=1;
				break;
			}	
		}
		if(flag)cout<<"NO\n";
		else cout<<"YES\n";
	}
}
signed main()
{
	quick_cin();
	int T;
	cin>>T;
	while(T--)solve();
	return 0;
}

2,treap

先看左右旋的操作:

 右旋zig:

(这里旋的叫法看自己理解,课本是叫左单选,实质是左孩子往右上旋,看着是往右上方移动了,所以叫右旋;)

 观察变化:

A的左孩子变成了B的右孩子,B的右孩子变成了A,根节点A变为B;

代码表示就是:

void zig(int &p)//为什么要引用,引用的话就会改变根节点;符合要求
{
	int tmp=tr[p].l;
	tr[p].l=tr[tmp].r;
	tr[tmp].r=p;
	p=tmp;
}

 左旋zag:

观察变化:

A的右孩子变成B的左孩子,B的左孩子变成A,根节点A变为B;

对应代码:

void zag(int &p)
{
	int tmp=tr[p].r;
	tr[p].r=tr[tmp].l;
	tr[tmp].l=p;
	p=tmp;
	pushup(tr[p].l),pushup(p);
}

      操作:

  1. 插入数值 xx。
  2. 删除数值 xx(若有多个相同的数,应只删除一个)。
  3. 查询数值 xx 的排名(若有多个相同的数,应输出最小的排名)。
  4. 查询排名为 xx 的数值。
  5. 求数值 xx 的前驱(前驱定义为小于 xx 的最大的数)。
  6. 求数值 xx 的后继(后继定义为大于 xx 的最小的数)
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++) 
#define rep2(i,a,n) for(register ll i=a;i<=n;i++) 
#define per1(i,n,a) for(register ll i=n;i>a;i--) 
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
#define endl "\n"
#define xiaoshu(a) setprecision(a)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const int N=1e5+10;
int n;
struct Node
{
	int l,r;
	int key,val;
	int cnt,size;
}tr[N];
int root,idx;
void pushup(int p)
{
	tr[p].size=tr[tr[p].l].size + tr[tr[p].r].size  +tr[p].cnt;
}
int get_node(int key)
{
	tr[++idx].key=key;
	tr[idx].val=rand();
	tr[idx].cnt=tr[idx].size=1;
	return idx;
}

void zig(int &p)//为什么要引用,引用的话就会改变根节点;符合要求
{
	int tmp=tr[p].l;
	tr[p].l=tr[tmp].r;
	tr[tmp].r=p;
	p=tmp;
	pushup(tr[p].r),pushup(p);
}
void zag(int &p)
{
	int tmp=tr[p].r;
	tr[p].r=tr[tmp].l;
	tr[tmp].l=p;
	p=tmp;
	pushup(tr[p].l),pushup(p);
}
void build()
{
	get_node(-INF);
	get_node(INF);
	root=1;
	tr[1].r=2;
	pushup(root);
	if(tr[1].val<tr[2].val)zag(root);
}
void insert(int &p,int key)
{
	if(!p)p=get_node(key);
	else if(tr[p].key==key)tr[p].cnt++;
	else if(tr[p].key>key)
	{
		insert(tr[p].l,key);
		if(tr[tr[p].l].val>tr[p].val)zig(p);
	}
	else
	{
		insert(tr[p].r,key);
		if(tr[tr[p].r].val>tr[p].val)zag(p);
	}
	pushup(p);
}
void remove(int &p,int key)
{
	if(!p)return;
	if(tr[p].key==key)
	{
		if(tr[p].cnt>1) tr[p].cnt--;
		else if(tr[p].l||tr[p].r)
		{
			if(!tr[p].r||tr[tr[p].l].val>tr[tr[p].r].val)
			{
				zig(p);
				remove(tr[p].r,key);
			}
			else 
			{
				zag(p);
				remove(tr[p].l,key);
			}
		}
		else p=0;
	}
	else if(tr[p].key>key)remove(tr[p].l,key);
	else remove(tr[p].r,key);
	pushup(p);
}
int get_rank_by_key(int p, int key)    // 通过数值找排名
{
    if (!p) return 0;   // 本题中不会发生此情况
    if (tr[p].key == key) return tr[tr[p].l].size + 1;
    if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
    return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
}

int get_key_by_rank(int p, int rank)   // 通过排名找数值
{
    if (!p) return INF;     // 本题中不会发生此情况
    if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
    if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
    return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}

int get_prev(int p, int key)   // 找到严格小于key的最大数
{
    if (!p) return -INF;
    if (tr[p].key >= key) return get_prev(tr[p].l, key);
    return max(tr[p].key, get_prev(tr[p].r, key));
}

int get_next(int p, int key)    // 找到严格大于key的最小数
{
    if (!p) return INF;
    if (tr[p].key <= key) return get_next(tr[p].r, key);
    return min(tr[p].key, get_next(tr[p].l, key));
}
signed main()
{
	quick_cin();
	cin>>n;
	while(n--)
	{
		int op,x;
		cin>>op>>x;
		if(op==1)insert(root,x);
		else if(op==2)remove(root,x);
		else if(op==3)cout<<get_rank_by_key(root,x)<<endl;
		else if(op==4)cout<<get_key_by_rank(root,x)<<endl;
		else if(op==5)cout<<get_prev(root,x)<<endl;
		else cout<<get_next(root,x)<<endl;
	}
	return 0;
}

3,fhq treap

4,minimal labels

题意:

给出n个节点和m条边的有向图,为这n个结点编号,要求:

1,所有边的子节点的编号要比其父节点大

2,最后的编号要是字典序最小

一开始以为按照节点顺序来一遍拓扑排序就行,然后从1开始对拓扑序列分配编号,但是wa了,wa83%,交cf发现是字典序的问题,没有准确保证字典序最小;

wa代码:

#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++) 
#define rep2(i,a,n) for(register ll i=a;i<=n;i++) 
#define per1(i,n,a) for(register ll i=n;i>a;i--) 
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
#define endl "\n"
#define xiaoshu(a) setprecision(a)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const int N=1e5+10;
int e[N],ne[N],h[N],d[N],idx;
int n,m;
int q[N],hh=0,tt=-1;
queue<int>qq;
queue<int>qq2;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int ans[N],topxl[N],k;
bool hs[N];
void topsort()
{
    rep2(i,1,n)
    {
        if(!d[i])qq2.push(i);
    }
    while(qq2.size())
    {
        qq.push(qq2.front());
        qq2.pop();
        while(qq.size())
        {
            int a=qq.front();
            q[hh++]=a;
            qq.pop();
            for(int i=h[a];i!=-1;i=ne[i])
            {
                int j=e[i];
                d[j]--;
                if(!d[j])qq.push(j);
            }
        }           
    }
    rep1(i,0,n)
    {
    //  cout<<q[i]<<" ";
        k++;
        ans[q[i]]=k;
    }
    //cout<<endl;
    rep2(i,1,n)cout<<ans[i]<<" ";
}
signed main()
{
    quick_cin();
    memset(h,-1,sizeof h);
    cin>>n>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        add(a,b);
        d[b]++;
    }
    topsort();
    return 0;
}

后来发现只要反向建图加大顶堆即可保证字典序最小;反向建图可以保证先给子结点分配编号,然后大顶堆可以保证结点从大到小分配,因为是倒着分配编号,所以大数能往后就靠后会保证子结点把大的编号占了,前边的父节点的编号就会小;

ad代码:

#include<bits/stdc++.h>
#define rep1(i,a,n) for(register ll i=a;i<n;i++) 
#define rep2(i,a,n) for(register ll i=a;i<=n;i++) 
#define per1(i,n,a) for(register ll i=n;i>a;i--) 
#define per2(i,n,a) for(register ll i=n;i>=a;i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define INF 0x3f3f3f3f
#define pb push_back
#define endl "\n"
#define xiaoshu(a) setprecision(a)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const int N=1e5+10;
int e[N],ne[N],h[N],d[N],idx;
int n,m;
int topxl[N],hh=0;
priority_queue<int>qq;
queue<int>qq2;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int ans[N],k;
bool hs[N];
void topsort()
{
	per2(i,n,1)
	{
		if(!d[i])qq2.push(i);
	}
	while(qq2.size())
	{
		qq.push(qq2.front());
		qq2.pop();
		while(qq.size())
		{
			int a=qq.top();
			topxl[++hh]=a;
			qq.pop();
			for(int i=h[a];i!=-1;i=ne[i])
			{
				int j=e[i];
				d[j]--;
				if(!d[j])qq.push(j);
			}
		}			
	}
	k=n;
	rep2(i,1,n)
	{
	//	cout<<topxl[i]<<" ";
		ans[topxl[i]]=k--;
	}
	rep2(i,1,n)cout<<ans[i]<<" ";
}
signed main()
{
	quick_cin();
	memset(h,-1,sizeof h);
	cin>>n>>m;
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		add(b,a);
		d[a]++;
	}
	topsort();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dull丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值