Gym 102606C CDQ分治优化 1D/1D 动态规划的转移

例题Problem - C - Codeforces

这是一个三维问题,通过对问题分析,可以得到非常板的式子

d p [ i ] = 1 + m a x j = 1 i − 1 d p j [ a j < a i ] [ b j < b i ] dp[i]=1+max^{i-1}_{j=1}dp_j[a_j<a_i][b_j<b_i] dp[i]=1+maxj=1i1dpj[aj<ai][bj<bi]

再然后就是考虑用CDQ将时间复杂度降下来。

对于一个区间 l l l~ r r r内的dp计算,首先通过对第一维的排序就可以保证dp[i]决策在1到i-1内。

将区间二分考虑,当i位于区间右边时候,需要先通过双指针来用左区间的决策更新i,这就要求左区间内所有的决策点dp都已经计算好了。所以在双指针计算之前,我们需要先递归处理左边。而为什么在双指针计算后再递归右边,可以这样考虑,右区间里大于i的dp值的更新需要求dp[i]已经完全计算完毕,但如果先递归右边区间,那么显然在递归右边区间的时候i处的dp值是没有计算完成的,还缺少左区间的这一部分决策点,但更新大于i的dp值只能通过递归右边,无法反悔更新,也就会出现计算遗漏。

这道题只需要用树状数组维护最大前缀值就可以了,另外有坑点就是空间需要开到3N,因为有每个点有3个值,然后离散后树状数组最大到3N。

#include<bits/stdc++.h>
using namespace std;
#define se second
#define fi first
#define LINF 0x3f3f3f3f3f3f3f3f
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define pii pair<ll,ll>
const int mod=998244353;
const int N=500005;
struct node{
	ull a,b,c;
	int id;
}nd[N],nd2[N];
int maxlen;
int maxn;
node temp1[N],temp2[N];
int dp[N];
bool cmp(node &a,node &b){
	return a.b<b.b;
}
int lowbit(int x){
	return x&(-x);
}
int tree[N];
int n;
void add(int x,int c){
	for(;x<=maxlen;x+=lowbit(x))tree[x]=max(tree[x],c);
}
void del(int x){
	for(;x<=maxlen;x+=lowbit(x))tree[x]=-1;
}
int querry(int x){
	int res=-1;
	for(;x;x-=lowbit(x))res=max(tree[x],res);
	return res;
}
void CDQ(int l,int r){
	if(l>=r)return;
	int mid=l+r>>1;
	CDQ(l,mid);
	for(int i=l;i<=mid;i++){
		temp1[i-l+1]=nd2[i];
	}
	for(int i=mid+1;i<=r;i++){
		temp2[i-mid]=nd2[i];
	}
	sort(temp1+1,temp1+mid-l+1+1,cmp);
	sort(temp2+1,temp2+r-mid+1,cmp);
	int i=1,j=1;
	while(j<=r-mid){
		while(i<=mid-l+1&&temp1[i].b<=temp2[j].b){
			add(temp1[i].c,dp[temp1[i].id]);i++;
		}
		dp[temp2[j].id]=max(dp[temp2[j].id],querry(temp2[j].c)+1);
		maxn=max(dp[temp2[j].id],maxn);
		j++;
	}
	for(int l1=1;l1<i;l1++)del(temp1[l1].c);
	CDQ(mid+1,r);
	return;
}
unsigned long long k1, k2;
unsigned long long CoronavirusBeats() {
    unsigned long long k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3<<23;
    k2 = k3 ^ k4 ^ (k3>>17) ^ (k4>>26);
    return k2 + k4;
}
void solve(){
	memset(tree,-1,sizeof tree);
	cin>>n>>k1>>k2;
	vector<ull>vec;
	for (int i = 1; i <= n; ++i) {
		nd[i].a = CoronavirusBeats();
		nd[i].b = CoronavirusBeats();
		nd[i].c = CoronavirusBeats();
		vec.push_back(nd[i].a);
		vec.push_back(nd[i].b);
		vec.push_back(nd[i].c);
	}
	sort(vec.begin(),vec.end());
	vec.erase(unique(vec.begin(),vec.end()),vec.end());
	for(int i=1;i<=n;i++){
		nd2[i].a=lower_bound(vec.begin(),vec.end(),nd[i].a)-vec.begin()+1;
		nd2[i].b=lower_bound(vec.begin(),vec.end(),nd[i].b)-vec.begin()+1;
		nd2[i].c=lower_bound(vec.begin(),vec.end(),nd[i].c)-vec.begin()+1;	
		nd2[i].id=i;
	}
	maxlen=vec.size();
	sort(nd2+1,nd2+1+n,[&](node a,node b){
		if(a.a!=b.a)return a.a<b.a;
		if(a.b!=b.b)return a.b<b.b;
		return a.c<b.c;
	});
	CDQ(1,n);
	cout<<maxn+1<<"\n";
	for(int i=1;i<=n;i++){
		cout<<dp[i]<<' ';
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值