P4849 寻找宝藏(模板:四维偏序)

本文详细解析了一道涉及四维偏序动态规划的问题,并通过三维偏序的思想进行了转移操作。利用stable_sort优化了排序过程,降低了出错风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

stable_sort 保平安。

解析

dp方程显而易见,关键就是如何进行这个四维偏序的转移。

考虑三维偏序(比如拦截导弹)我们是如何做的?
先按照第一维排序,然后分治解决前一半,接下来把前一半的第一维看成0,第二维看成1,按照第二维排序后,用所有的0对1进行转移。

本题也延续这样的思想,先按照第一维排序,前一半看成0,后一半看成1,然后再对其进行一次三维偏序的转移,有所不同就是只有打上0标记的才可以加入树状数组,打上1标记的才可以接受转移。

换句话说,就是只能让0,0向1,1转移。

注意排序一定要排彻底!
stable_sort 是好东西,在常数可以接受的情况下大大降低写错的风险。
其实主要出错就是重点的时候,注意一下把cmp函数写好也不会有问题。

代码

(毕竟是模板,就贴的是不用 stable_sort 也能过的)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=8e4+100;
const int C=205;
const int mod=998244353;

inline ll read(){
    ll x(0),f(1);char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
int n,m;
#define pr pair<ll,ll>
#define mkp make_pair
pr f[N];
int q[N],cnt;
void operator += (pr &a,pr b){
	if(a.first<b.first) a=b;
	else if(a.first==b.first) (a.second+=b.second)%=mod;
}
inline void add(int p,pr o){
	for(;p<=cnt;p+=p&-p) f[p]+=o;
	return;
}
inline pr ask(int p){
	pr res=mkp(0,1);
	for(;p;p-=p&-p) res+=f[p];
	return res;
}
inline void clear(int p){
	for(;p<=cnt;p+=p&-p) f[p]=mkp(0,1);
}
struct node{
	int x[5],v;
	pr dp;
	bool op;
}p[N];
inline void print(node o,bool op=1){
	printf("[(%d %d %d %d) dp=(%lld %lld) v=%d op=%d] ",
	o.x[1],o.x[2],o.x[3],o.x[4],o.dp.first,o.dp.second,o.v,o.op);
	if(op) putchar('\n');
	return;
}
bool cmp1 (const node &a,const node &b){
	if(a.op!=b.op) return a.op>b.op;
	if(a.x[1]!=b.x[1]) return a.x[1]<b.x[1];
	else if(a.x[2]!=b.x[2]) return a.x[2]<b.x[2];
	else if(a.x[3]!=b.x[3]) return a.x[3]<b.x[3];
	else if(a.x[4]!=b.x[4]) return a.x[4]<b.x[4];
	else return a.dp<b.dp;
}
bool cmp2 (const node &a,const node &b){
	if(a.x[2]!=b.x[2]) return a.x[2]<b.x[2];
	else if(a.x[3]!=b.x[3]) return a.x[3]<b.x[3];
	else if(a.x[4]!=b.x[4]) return a.x[4]<b.x[4];
	else if(a.x[1]!=b.x[1]) return a.x[1]<b.x[1];
	else return a.op>b.op;
}
bool cmp3 (const node &a,const node &b){
	if(a.x[3]!=b.x[3]) return a.x[3]<b.x[3];
	else if(a.x[4]!=b.x[4]) return a.x[4]<b.x[4];
	else if(a.x[1]!=b.x[1]) return a.x[1]<b.x[1];
	else if(a.x[2]!=b.x[2]) return a.x[2]<b.x[2];
	else return a.dp<b.dp;
}

void solve2(int l,int r){
	if(l==r){
		//p[l].dp+=mkp(p[l].v,1);
		return;
	}
	int mid=(l+r)>>1;
	solve2(l,mid);
	sort(p+l,p+mid+1,cmp3);
	sort(p+mid+1,p+r+1,cmp3);	
	int pl=l;
	for(int i=mid+1;i<=r;i++){
		while(pl<=mid&&p[pl].x[3]<=p[i].x[3]){
			if(p[pl].op){
				add(p[pl].x[4],p[pl].dp);
			}
			++pl;
		}
		if(!p[i].op){
			pr o=ask(p[i].x[4]);
			o.first+=p[i].v;
			if(o.first!=p[i].v) p[i].dp+=o;
		}
	}
	for(int i=l;i<pl;i++){
		if(p[i].op) clear(p[i].x[4]);
	}
	//printf("    solve2: ");
	//for(int i=l;i<=r;i++) print(p[i],i==r);
	sort(p+mid+1,p+r+1,cmp2);
	solve2(mid+1,r);
	return;
}
void solve1(int l,int r){
	if(l==r) return;
	int mid=(l+r)>>1;
	solve1(l,mid);
	//sort(p+l,p+mid+1,cmp2);
	//sort(p+mid+1,p+r+1,cmp2);
	for(int i=l;i<=mid;i++) p[i].op=1;
	for(int i=mid+1;i<=r;i++) p[i].op=0;
	sort(p+l,p+r+1,cmp2);
	//printf("\nsolve1: ");
	//for(int i=l;i<=r;i++) print(p[i],i==r);
	solve2(l,r);
	sort(p+l,p+r+1,cmp1);
	solve1(mid+1,r);
	return;
}

signed main(){
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    #endif
	n=read();m=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=4;j++) p[i].x[j]=read();
		p[i].v=read();
		q[++cnt]=p[i].x[4];
		p[i].dp=mkp(p[i].v,1);
	}
	sort(q+1,q+1+cnt);
	cnt=unique(q+1,q+1+cnt)-q-1;
	for(int i=1;i<=n;i++){
		p[i].x[4]=lower_bound(q+1,q+1+cnt,p[i].x[4])-q;
	}
	sort(p+1,p+1+n,cmp1);
	solve1(1,n);
	pr ans=mkp(0,1);
	for(int i=1;i<=n;i++) ans+=p[i].dp;
	printf("%lld\n%lld\n",ans.first,ans.second);
    return 0;
}
/*
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值