寻找宝藏(CDQ四维)

题意要求:我们要求出最大价值的四维上升序列,且统计最大价值四维上升序列的数量。

这显然是一道dp,那怎么转移呢?

当 xi<xj && yi<yj && zi<zj && ki<kj 时, dp[j] = max(dp[i] + val[j],dp[j]);这样最大价值就可以转移出来了,至于数量我们可以将dp开成pair,第一维转移价值,第二维统计数量。

那么我们该如何遍历转移呢?

我们会发现他转移的条件显然是一个四维偏序,我们可以通过套两次cdq来解决

注意:第四维存在树状数组中所以需要离散化,dp转移需要cdq强制左中右转移。

#include<bits/stdc++.h>
using namespace std;

#define ull unsigned long long
#define ll long long
#define edl '\n'

const int N = 1e6 + 10;
const int M = 1e3 + 10;

const ll mod = 998244353;
ll n,m,q;
string s;

struct QWQ
{
	ll x,y,z,k,sum,id;
	bool ck;
}a[N],b[N],c[N];

bool cmp(QWQ A,QWQ B)
{
	if(A.x != B.x) return A.x < B.x;
	if(A.y != B.y) return A.y < B.y;
	if(A.z != B.z) return A.z < B.z;
	if(A.k != B.k) return A.k < B.k;
}

bool cmp1(QWQ A,QWQ B)
{
	if(A.y != B.y) return A.y < B.y;
	if(A.z != B.z) return A.z < B.z;
	if(A.k != B.k) return A.k < B.k;
	if(A.x != B.x) return A.x < B.x;
}

bool cmp2(QWQ A,QWQ B)
{
	if(A.z != B.z) return A.z < B.z;
	if(A.k != B.k) return A.k < B.k;
	if(A.x != B.x) return A.x < B.x;
	if(A.y != B.y) return A.y < B.y;
}

template <typename T>
struct Fenwick {
	int n;
	std::vector<T> a;
	
	Fenwick(int n_ = 0) {
		init(n_);
	}
	
	void init(int n_) {
		n = n_;
		a.assign(n, T{});
	}
	
	void add(int x, T &v) {
		v.second %= mod;
		for (int i = x + 1; i <= n; i += i & -i) {
			if(a[i - 1].first == v.first)
			{
				a[i - 1].second += v.second;
				a[i - 1].second %= mod;
			}
			if(v.first > a[i - 1].first) a[i - 1] = v;
		}
	}
	//[0,x)
	T sum(int x) {
		T ans{};
		for (int i = x; i > 0; i -= i & -i) {
			if(a[i - 1].first == ans.first)
			{
				ans.second += a[i - 1].second;
				ans.second %= mod;
			}
			if(a[i - 1].first > ans.first) ans = a[i - 1];
		}
		return ans;
	}
	//左闭右开
	void clear(int x) {
		for (int i = x + 1; i <= n; i += i & -i) {
			a[i - 1].first = a[i - 1].second = 0;
		}
	}
};

Fenwick<pair<ll,ll>>f(N);
pair<ll,ll> dp[80001];
ll sum;
void cdq1(int l,int r)
{
	if(l == r) return;
	int mid = (l + r) >> 1;
	cdq1(l,mid);
	for(int i = l; i <= r; i ++ ) c[i] = b[i];
	sort(c + l,c + mid + 1,cmp2);
	sort(c + mid + 1,c + r + 1,cmp2);
	int i = l;
	int j = mid + 1;
	while(j <= r)
	{
		if(c[j].ck)
		{
			++j;
			continue;
		}
		else
		{
			while(i <= mid && c[i].z <= c[j].z)
			{
				if(c[i].ck) f.add(c[i].k,dp[c[i].id]);
				++i;
			}
		}
		pair<ll,ll> temp = f.sum(c[j].k + 1);
		temp.first += c[j].sum;
		if(temp.first == dp[c[j].id].first) dp[c[j].id].second=(dp[c[j].id].second+temp.second)%mod;
		if(temp.first > dp[c[j].id].first) dp[c[j].id]=temp;
		sum = max(sum,dp[c[j].id].first);
		++j;
	}
	for(j = l; j < i; j ++ ) if(c[j].ck) f.clear(c[j].k);
	cdq1(mid + 1,r);
}


void cdq(int l,int r)
{
	if(l == r) return;
	int mid = (l + r) >> 1;
	cdq(l,mid);
	for(int i = l; i <= r; i ++ ) b[i] = a[i];
	for(int i = l; i <= mid; i ++ ) b[i].ck = 1;
	sort(b + l,b + r + 1,cmp1);
	cdq1(l,r);
	cdq(mid + 1,r);
}
ll OK[N];
ll cnt1,cnt2;
int find(ll x) { return lower_bound(OK + 1, OK + 1 + cnt2, x) - OK; }
void solve()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ ) cin >> a[i].x >> a[i].y >> a[i].z >> a[i].k >> a[i].sum;
	sort(a + 1, a + n + 1,cmp);
	int nn = 0;
	for(int i = 1; i <= n; i ++ )
	{
		if(a[i].x == a[nn].x && a[i].y == a[nn].y && a[i].z == a[nn].z && a[i].k == a[nn].k) a[nn].sum += a[i].sum;
		else
		{
			a[++nn] = a[i];
			a[nn].id = nn;
		}
	}
	for(int i = 1; i <= nn; i ++ )
	{
		OK[++cnt1] = a[i].k;
		OK[++cnt1] = a[i].k + 1;
		dp[i].second = 1;
		dp[i].first = a[i].sum;
	}
	sort(OK + 1, OK + 1 + cnt1);
	for (int i = 1; i <= cnt1; i++)
		if (i == 1 || OK[i - 1] < OK[i])
			OK[++cnt2] = OK[i];
	for(int i = 1; i <= nn; i ++ )
	{
		a[i].k = find(a[i].k);
	}
	cdq(1,nn);
	cout << sum << edl;
	ll ok = 0;
	for(int i = 1; i <= nn; i ++ )
	{
		if(dp[i].first == sum)
		{
			ok += dp[i].second;
			ok %= mod;
		}
	}
	cout << ok << edl;
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	int t = 1;
//	cin >> t;
	while(t -- ) solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值