1. 程式人生 > >免費的餡餅(二維偏序)(樹狀陣列版)

免費的餡餅(二維偏序)(樹狀陣列版)

https://vjudge.net/contest/261263#problem/B(題目連結)

因為一秒可以走1或2步或不走。

我們可以看成半秒走1步或不走。

dp[i]表示接到第i塊餅時最大的分數值

現在有兩塊餅它們下落的時間為ti,tj,位置為pi,pj;

假設ti > tj;

只有ti - tj >= |pi - pj| 時dp[j]可以轉移到dp[i];

當pi > pj 時 ti -tj > pi - pj    

ti  - pi > tj - pj;

 當pi < pj 時 ti -tj > pj - pi    

ti  + pi > tj + pj;

當pi > pj , ti -tj > pi - pj時 pj - pi < 0 , ti - tj > pj - pi;

 當pi < pj , ti -tj > pj - pi時 pi - pj<0, ti -tj > pi - pj ;

所以dp[j]可以轉移到dp[i]的條件是

ti  + pi > tj + pj;並且ti  - pi > tj - pj;

這是個二維偏序問題

我們把v1 看成 ti  + pi, 把v2看成ti  - pi

把v1看成第一維從小到大排序,v2 看成第二維用資料結構維護

先把v2離散化

在把所有餅按v1從小到大排序,再在以離散化後的v2為下標,dp為值的一棵值域線段樹(我寫的樹狀陣列)上找能更新到這個點的最大的dp值。

樹狀陣列維護區間最大值,支援修改和查詢操作

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int w, n, sval[100005], ans, f[100005], dp[100005];
struct node
{
	int v1, v2, val;
};
node q[100005];
bool cmp(const node a,const node b)
{
	return a.v1 < b.v1;
}
int qurey(int a)
{
	int rt = 0;
	for(int i = a; i; i -= i &(-i))
	{
		 rt = max(rt,f[i]);
	}
	return rt;
}
void modify(int a,int b)
{
	for(int i = a; i <= n; i += i&(-i))
	{
		f[i] = max(f[i],b);
	}
}
int main()
{
	cin >> w >> n;
	for(int i = 1; i <= n; i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
	    q[i].v1 = a * 2 + b;
	    q[i].v2 =  a * 2 -  b;
	    sval[i] = q[i].v2;
		q[i].val = c;
	}
	sort(sval+1,sval+1+n);
	int m = unique(sval+1,sval+1+n) - sval;
	for(int i = 1; i <= n; i++)
	{
		q[i].v2 = lower_bound(sval+1,sval+1+m,q[i].v2) - sval;
	}
    sort(q+1,q+1+n,cmp);
    for(int i = 1; i <= n; i++)
    {
    	int ha = qurey(q[i].v2);
    	dp[i] = ha + q[i].val;
    	modify(q[i].v2,dp[i]);
    }
    for(int i = 1; i <= n; i++)
    ans = max(dp[i],ans);
    cout << ans << endl;
    return 0;
}