1. 程式人生 > >[POI2005]AUT-The Bus

[POI2005]AUT-The Bus

題目描述:

  Byte City 的街道形成了一個標準的棋盤網路 – 他們要麼是北南走向要麼就是西東走向. 北南走向的路口從 1 到 n編號, 西東走向的路從1 到 m編號. 每個路口用兩個數(i, j) 表示(1 <= i <= n, 1 <= j <= m). Byte City裡有一條公交線, 在某一些路口設定了公交站點. 公交車從 (1, 1) 發車, 在(n, m)結束.公交車只能往北或往東走. 現在有一些乘客在某些站點等車. 公交車司機希望在路線中能接到儘量多的乘客.幫他想想怎麼才能接到最多的乘客.

  看完題目後我很高興,我以為這是到大水題,只不就是一個O(nm)的DP嘛,有什麼難的。但當我看到資料範圍1n109

1≤m≤1091k105 的時候,我就笑不出來了,我立刻想到這一定是一個O(k2)的DP,通過資料結構優化為O(klogk)

我首先想到的是將整張圖按照與對角線平行的線分為n+m-1層,逐層遞推,但我很快發現不行,(在笛卡爾座標系下)如兩個點(2,3)和(4,2) ,點2的所在層數比點1大1,按照我的方法點2應該可以從點1轉移,但實際上不行,點1不能向下走到點2

然後我就想到了最長不下降子序列的樹狀陣列優化法,我將x排序後,將y做一個最大子序列(注:不是最長不下降子序列)不就可以了嗎?考慮到1≤m≤109所以要離散化

操作起來,就是dp[i]表示,以x排序後的以第i個點為最後一個節點所能達到的最大值
轉移為在1 - (i-1)中找到y值不大於第i個點y值的點,在其中取最大值再加上p[i],就是dp[i]的值,樹狀陣列優化法較為常見,就不再贅述

實現如下:

#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstdlib>
#define lowbit(x) x&-x
using
namespace std; typedef long long ll; inline int read() { register int p(1),a(0);register char ch=getchar(); while((ch<'0'&&ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') p=-1,ch=getchar(); while(ch>='0'&&ch<='9') a=a*10+ch-48,ch=getchar(); return a*p; } const int N=1000100; struct node { int x,y,p,hao; bool operator < (const node xx)const { return x==xx.x?y<xx.y:x<xx.x; } }a[N]; inline int max(int x,int y){return x<y?y:x;} int f[N],n,m,k,tt[N],maxn[N],len,ans=0,temp; void update(int p,int x) { for(int i=p;i<=len;i+=lowbit(i)) maxn[i]=max(x,maxn[i]); } int query(int p) { int ans=0; for(int i=p;i>=1;i-=lowbit(i)) ans=max(ans,maxn[i]); return ans; } int main() { n=read(),m=read(),k=read(); for(int i=1;i<=k;++i) a[i].x=read(),a[i].y=read(),a[i].p=read(); sort(a+1,a+1+k); for(int i=1;i<=k;++i) tt[i]=a[i].y; sort(tt+1,tt+1+k); len=unique(tt+1,tt+1+k)-tt-1; for(int i=1;i<=k;++i) a[i].hao=lower_bound(tt+1,tt+1+len,a[i].y)-tt-1; for(int i=1;i<=k;++i) { ans=max(ans,temp=a[i].p+query(a[i].y)); update(a[i].y,temp); } printf("%d\n",ans); return 0; }