2131: 免费的馅饼
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 408 Solved: 245
[ Submit][ Status][ Discuss]
Description
Input
第一行是用空格隔开的二个正整数,分别给出了舞台的宽度W(1到10^8之间)和馅饼的个数n(1到10^5)。 接下来n行,每一行给出了一块馅饼的信息。由三个正整数组成,分别表示了每个馅饼落到舞台上的时刻t[i](1到10^8秒),掉到舞台上的格子的编号p[i](1和w之间),以及分值v[i](1到1000之间)。游戏开始时刻为0。输入文件中同一行相邻两项之间用一个空格隔开。输入数据中可能存在两个馅饼的t[i]和p[i]都一样。
Output
一个数,表示游戏者获得的最大总得分。
Sample Input
Sample Output
记得这道题有个简单版的,非常经典,可以直接O(WT)的暴力(W是宽度,T是最后一个馅饼掉下来的时间)
但是这题W和T都非常大,肯定不行
……
val[i]表示第i个馅饼的分值,loc[i]表示位置,t[i]表示时间
考虑O(n²)的DP, dp[i]表示接住第i个馅饼之后能取得的最大分数
那么dp[i] = max(dp[j] (|loc[i]-loc[j]|<=2*(t[i]-t[j])))+val[i]
非常简单,但是n=100000还是会超时
……
其实这种取前驱最大值的dp,一般都可以树状数组维护,这样复杂度就做到O(nlogn)了
由上面的转移方程可得,接完x馅饼之后能接到y馅饼的条件是|loc[y]-loc[x]|<=2*(t[y]-t[x])
也就是loc[y]-loc[x]<=2*(t[y]-t[x]) && loc[x]-loc[y]<=2*(t[y]-t[x])
移项得2t[x]+p[x]<=2t[y]+p[y] && 2t[x]-p[x]<=2t[y]-p[y]
令a[x] = 2t[x]+p[x], b[x] = 2t[x]-p[x]
这样的话就相当于对于馅饼x和y,如果a[x]<=a[y] && b[x]<=b[y]那么接完x馅饼之后能接到y馅饼,否则不行
这样就可以轻松用树状数组维护前驱最大值
按b从小到大遍历所有馅饼,对于第i个馅饼求出<=a[i]的前缀最大值并更新即可
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef struct Point
{
int x, y;
int val;
bool operator < (const Point &b) const
{
if(y<b.y || y==b.y && x<b.x)
return 1;
return 0;
}
}Point;
Point s[100005];
int cnt, Hash[100005], tre[100005];
void Update(int k, int val)
{
while(k<=cnt)
{
tre[k] = max(val, tre[k]);
k += k&-k;
}
}
int Query(int k)
{
int ans = 0;
while(k)
{
ans = max(ans, tre[k]);
k -= k&-k;
}
return ans;
}
int main(void)
{
int n, i, t, p, now;
scanf("%*d%d", &n);
for(i=1;i<=n;i++)
{
scanf("%d%d%d", &t, &p, &s[i].val);
s[i].x = 2*t+p;
s[i].y = 2*t-p;
Hash[i] = s[i].x;
}
sort(s+1, s+n+1);
sort(Hash+1, Hash+n+1);
cnt = unique(Hash+1, Hash+n+1)-(Hash+1);
for(i=1;i<=n;i++)
{
p = lower_bound(Hash+1, Hash+cnt+1, s[i].x)-Hash;
now = Query(p)+s[i].val;
Update(p, now);
}
printf("%d\n", Query(cnt));
return 0;
}