CF56E Domino Principle

CF56E Domino Principle

题意:
给你一些多米诺骨牌,每个骨牌都有一个高度 h h h,它倒下时会放到 [ x + 1 , x + h − 1 ] [x+1,x+h-1] [x+1,x+h1],在 x x x轴上从左到右排列在一起,问,每一个倒向右边的时候会压倒多少个骨牌?给定的骨牌并不是按照 x x x从小到大的顺序排列的。

思路:线段树优化DP
先对骨牌按 x x x排序,然后从右往左推, d p i dp_i dpi表示放倒 i i i一共放倒的骨牌数, d p i = m a x ( d p j + j − i ) , ( x i + 1 ≤ j ≤ x i + h i − 1 ) dp_i=max(dp_j+j-i),(x_i+1\le j\le x_i+h_i-1) dpi=max(dpj+ji),(xi+1jxi+hi1),时间复杂度 O ( n 2 ) O(n^2) O(n2),我们用线段树优化 d p dp dp,每次二分出第 i i i块牌能压倒的最后一块牌的编号 r r r,每次查询 d p j + j ( 1 ≤ j ≤ r ) dp_j+j(1\le j\le r) dpj+j(1jr)的最大值,再减去 i i i,最后修改为 d p i + i dp_i+i dpi+i,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define per(i, a, b) for(int i = (a); i >= (b); --i)
#define debug(x) cout << #x << " = " << x << endl;
#define ll long long
#define endl '\n'
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(), (x).end()
const int N = 1e5 + 10, M = 2 * N;
const ll mod = 1e9 + 7;
const ll inf = 0x3f3f3f3f;

int n;
struct dom {
	int x, h, id;
	bool operator<(const dom &t) const { return x < t.x; }
} a[N];

int dp[N];

#define ls u << 1
#define rs u << 1 | 1

struct node {
	int l, r;
	int v;
} tr[N * 4];

void pushup(int u) { tr[u].v = max(tr[ls].v, tr[rs].v); }

void build(int u, int l, int r) {
	if(l == r)
		tr[u] = {l, r};
	else {
		tr[u] = {l, r};
		int mid = l + r >> 1;
		build(ls, l, mid), build(rs, mid + 1, r);
		pushup(u);
	}
}

void modify(int u, int x, int v) {
	if(tr[u].l == x && tr[u].r == x) {
		tr[u].v = v;
	} else {
		int mid = tr[u].l + tr[u].r >> 1;
		if(x <= mid) modify(ls, x, v);
		if(x > mid) modify(rs, x, v);
		pushup(u);
	}
}

int query(int u, int l, int r) {
	if(tr[u].l >= l && tr[u].r <= r) {
		return tr[u].v;
	} else {
		int mid = tr[u].l + tr[u].r >> 1;
		int res = -inf;
		if(l <= mid) res = max(res, query(ls, l, r));
		if(r > mid) res = max(res, query(rs, l, r));
		return res;
	}
}

int ans[N];

void solve() {
	cin >> n;
	rep(i, 1, n) {
		cin >> a[i].x >> a[i].h;
		a[i].id = i;
	}
	sort(a + 1, a + 1 + n);
	build(1, 1, n);

	// rep(i,1,n) cout<<a[i].x<<' '<<a[i].h<<' '<<a[i].id<<endl;

	dp[n] = 1;
	ans[a[n].id] = 1;
	modify(1, n, n + 1);

	per(i, n - 1, 1) {
		int l = i, r = n;
		while(l < r) {
			int mid = l + r + 1 >> 1;
			if(a[i].x + a[i].h > a[mid].x)
				l = mid;
			else
				r = mid - 1;
		}
		if(i == r)
			dp[i] = 1;
		else
			dp[i] = query(1, i, r) - i;
		modify(1, i, dp[i] + i);
		ans[a[i].id] = dp[i];
	}
	rep(i, 1, n) cout << ans[i] << ' ';
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	// int _;cin>>_;while(_--)
	solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值