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);
}
操作:
- 插入数值 xx。
- 删除数值 xx(若有多个相同的数,应只删除一个)。
- 查询数值 xx 的排名(若有多个相同的数,应输出最小的排名)。
- 查询排名为 xx 的数值。
- 求数值 xx 的前驱(前驱定义为小于 xx 的最大的数)。
- 求数值 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;
}