2019ICPC南京网络赛F-Greedy Sequence(静态主席树+DP)

题目链接:https://nanti.jisuanke.com/t/41303

题目大意:给出一个n个元素的数组A,1<=A[i]<=n,找出n个串S满足5个条件:

1:S[i][1]=i   2:S[i][j]<=S[i][j-1](就是单调不增)    3:S中相邻元素在数组A中的下标绝对值<=k   4:S的字典序最大   5:A中的每个元素只能使用一次。输出每个串的长度

思路:首先一个题目中隐含的条件:A中所有元素不相同,这一点可以从条件1看出来。

然后就是很简单了,由于所有元素不相同,那么我们可以直接贪心的找[pos-k,pos+k]中的最大的<k的数。对于每次找到的数,再记录一下答案,避免重复查找就好了。

ACCode:

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
      
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
 
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
      
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)

struct ChairTree{
	int Rt[MAXN<<5],Lc[MAXN<<5],Rc[MAXN<<5],Sum[MAXN<<5];
	int NodeCnt;
	
	void Build(int l,int r,int rt){
		rt=++NodeCnt;
		if(l==r) return ;
		int mid=(l+r)>>1;
		Build(l,mid,Lc[rt]);Build(mid+1,r,Rc[rt]);
	}
	int Modify(int pos,int l,int r,int rt){
		int Nrt=++NodeCnt;
		Lc[Nrt]=Lc[rt];Rc[Nrt]=Rc[rt];Sum[Nrt]=Sum[rt]+1;
		if(l==r) return Nrt;
		int mid=(l+r)>>1;
		if(pos<=mid) Lc[Nrt]=Modify(pos,l,mid,Lc[Nrt]);
		if(pos>mid) Rc[Nrt]=Modify(pos,mid+1,r,Rc[Nrt]);
		return Nrt;
	}
	int Query(int ql,int qr,int l,int r,int k){//[ql,qr]种找最后一个<=k的元素 
		if(l==r) return l;
		int xl=Sum[Lc[qr]]-Sum[Lc[ql]],xr=Sum[Rc[qr]]-Sum[Rc[ql]];
		int mid=(l+r)>>1;
		int ans=INF32;
		if(mid>=k){//只会出现在[l,mid]
			if(xl) ans=Query(Lc[ql],Lc[qr],l,mid,k);
			else return ans;
		}
		else{// mid<k
		//[l,mid]和[mid+1,r]都可能出现
			if(xr) ans=Query(Rc[ql],Rc[qr],mid+1,r,k);
			if(ans!=INF32) return ans;
			if(xl) ans=Query(Lc[ql],Lc[qr],l,mid,k);
			return ans;
		}
	}
	void Intt(int l,int r){
		NodeCnt=0;
		Build(l,r,Rt[0]);
	}
	void Update(int pos,int l,int r,int i){
		Rt[i]=Modify(pos,l,r,Rt[i-1]);
	}
	int Ans(int ql,int qr,int l,int r,int k){//查找最大的<=r的元素 
		return Query(Rt[ql-1],Rt[qr],l,r,k);
	}
};
ChairTree CT;
int A[MAXN],Vis[MAXN];
int Ans[MAXN];
int n,k;

void DFS(int pos){
	int val=A[pos];
	if(Ans[val]) return ;
	int mi=max(1,pos-k),mx=min(n,pos+k);//查询[mi,mx]的最大的<A[pos]元素 
	int res;
	if(val==1) res=INF32;
	else res=CT.Ans(mi,mx,1,n,A[pos]-1);
	if(res==INF32){
		Ans[val]=1;//当前没有更小的了 
	}
	else{
		DFS(Vis[res]);//更新更小的 
		Ans[val]=Ans[res]+1;//resi是可以选择的元素 
	}
}
int main(){
	int T;scanf("%d",&T);
	while(T--){
		clean(Ans,0); 
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;++i){
			scanf("%d",&A[i]);
			Vis[A[i]]=i;
		}CT.Intt(1,n);
		for(int i=1;i<=n;++i) CT.Update(A[i],1,n,i);
		for(int i=1;i<=n;++i) DFS(i);
		printf("%d",Ans[1]);
		for(int i=2;i<=n;++i) printf(" %d",Ans[i]);printf("\n");
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值