2019icpc南京网络赛F. Greedy Sequence(线段树/主席树)

https://nanti.jisuanke.com/t/41303

题意:就是构造字符串 ,给定一个序列(无重复,长度为n),现在让你构造n个序列问你这n个序列的长度,限制条件是这样的:第一个序列的开头是1,第二个序列的开头是2,依次类推,但是每个序列是单调递减的,并且每个序列往后扩必须满足这个已知数字在原序列位置的|posj-k|--|posj+k|的区间内找扩展的数,看样例理解吧。

题解:因为构造的序列是单调递减的并且必须在区间内找,即转换为在区间【l,r】内找小于X的最大的数即可

           所以对原序列排序在依次插入,依次查询,目的是在到查 i 时,区间里只有小于 i 的数,在考虑区间即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int a[maxn];
struct node{
	int x;
	int id;
}s[maxn];
bool cmp(node a,node b){
	return a.x<b.x;
}
int mx[maxn*4];
void build(int l,int r,int p){
	if(l==r){
		mx[p]=0; return ;
	}
	int mid=(l+r)/2;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
void update(int l,int r,int id,int pos,int p){
	if(l==r){
		mx[p]=max(mx[p],pos); return ;
	}
	int mid=(l+r)/2;
	if(id<=mid) update(l,mid,id,pos,p<<1);
	else update(mid+1,r,id,pos,p<<1|1);
	mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
int quert(int l,int r,int L,int R,int p){
	if(L<=l&&r<=R) {
		return mx[p];
	}
	int mid=(l+r)/2;
	int ans=0;
	if(L<=mid) ans=max(ans,quert(l,mid,L,R,p<<1));
	if(R>mid) ans=max(ans,quert(mid+1,r,L,R,p<<1|1));
	return ans;
}
int ans[maxn];
int main(){
	int t; cin>>t;
	while(t--){
		int n,k; cin>>n>>k;
		for(int i=1;i<=n;i++){
			cin>>s[i].x; s[i].id=i;
		}
		sort(s+1,s+n+1,cmp);
		build(1,n,1);
		for(int i=1;i<=n;i++){
			int l=s[i].id-k; if(l<=1) l=1;
			int r=s[i].id+k; if(r>=n) r=n;
			int p=quert(1,n,l,r,1);
		//	cout<<p<<" -- "<<endl;
			if(p==0) ans[i]=1;
			else ans[i]=ans[p]+1;
			if(i<n) cout<<ans[i]<<" ";
			else cout<<ans[i]<<endl;
			update(1,n,s[i].id,s[i].x,1);
		}
	}
	return 0;
} 

//网上有个老哥是主席树写法学习了一下https://www.cnblogs.com/xusirui/p/11462666.html

老哥的思路就是:当前区间[l,r]的sum(数字个数)为0则剪枝,l==r时,如果l<k说明l是k的前驱,否则说明不存在k的前驱,查找时,如果k<=m+1(m=(l+r)/2),返回递归求左区间的答案(注意为什么是m+1,因为当k<=m+1时,小于k的值必定只可能出现在左区间[l,m]中),否则查找右区间,当递归右区间有解,则必为最优解,直接返回这个解即可,当右区间无解,才继续递归左区间求解。  主席树上剪枝搜索。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
struct node{
	int l;
	int r;
	int sum;
	int mx; 
}T[maxn*4];
int a[maxn],root[maxn],cnt;
void update(int l,int r,int &x,int y,int pos){
	T[++cnt]=T[y];
	T[cnt].sum++;
	T[cnt].mx=(pos,T[cnt].mx);
	x=cnt;      
	if(l==r)  return ;
	int mid=(l+r)/2;
	if(pos<=mid) update(l,mid,T[x].l,T[y].l,pos);
	else update(mid+1,r,T[x].r,T[y].r,pos);
}
int quert(int l,int r,int x,int y,int k){//在区间内找小于K的最大的 ;
    if(T[y].sum-T[x].sum==0) return 0;
    if(l==r){
    	if(l<k) return l;
    	return 0;
	}
	int mid=(l+r)/2;
	if(k<=mid+1) return quert(l,mid,T[x].l,T[y].l,k); 
	int res=quert(mid+1,r,T[x].r,T[y].r,k);
	if(res!=0) return res;
	else return quert(l,mid,T[x].l,T[y].l,k); 

}
struct num_node{
	int x;
	int id;
}s[maxn];
bool cmp(num_node a,num_node b){
	return a.x<b.x;
}
int ans[maxn];
int main(){
	int t; cin>>t;
	while(t--){
	cnt=0;//注意 ,负责RE
	int n,k; cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>s[i].x; s[i].id=i;
		update(1,n,root[i],root[i-1],s[i].x);
	}
	sort(s+1,s+n+1,cmp);
	for(int i=1;i<=n;i++){
		int l=s[i].id-k; if(l<=1) l=1;
		int r=s[i].id+k; if(r>=n) r=n;
		int p=quert(1,n,root[l-1],root[r],s[i].x);
		if(p==0) ans[i]=1;
		else ans[i]=ans[p]+1;
		if(i<n) cout<<ans[i]<<" ";
		else cout<<ans[i]<<endl;	 
	}
    }
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值