1. 程式人生 > >【NOIP2015】推銷員

【NOIP2015】推銷員

推(chuan)銷員

分析

這裡主要闡述一下我的分析思路。
看起來挺直觀的。

最初的想法,我們列舉每一個最遠點mxp的位置,然後對之前的a進行排序。
那麼以mxp為最遠點,選x個的最大疲勞值為:
2s[mxp]+a[mxp]+(x1a)
這樣的複雜度為O(n2logn),考試時就這樣拿了個60pt。

但是,我們要嘗試發現這道題的特性,來進行時間上的優化。
根據極大化思想,我們要儘可能排除不影響答案的(mxp,x)
x一定時,設i<ji沒有j優,這等價於:
2s[i]+a[i]+(ix1a)<2s[j]+a[j]

+(jx1a)
w[i]=2s[i]+a[i]
w[i]w[j]<(jx1a)(ix1a)
x增大的時候,例如x變大到x+1,發現(jxa)(ixa)的值一定是遞增的,因為jxa一定是jx1a多一個數,i也一樣,而i能選擇到的j也能選擇得到。
所以我們得到了決策單調性:對於xi<ji沒有j優,那麼隨著x的增大,i仍然沒有j優,所以對於x的詢問的決策點會非嚴格單調遞增。

接下來,很容易想到用單調佇列什麼的進行維護。
但怎麼嘗試都覺得不行……

這時候就一定要跳出來啦。
根據決策單調性這個重要的特點,考慮換一種思考的角度。

假如當前x1這個詢問我們決策點為cur,答案為res,現在要求x這個詢問的決策點和答案。
我們有兩種方法:
①在cur之前選擇一個沒有選擇過的點iΔ=a[i]
②在cur之後選擇一個決策點,Δ=2s[i]+a[i]2s[cur]
用兩個堆實現即可。

有點意思。

程式碼

#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;

#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define x first #define y second #define mp make_pair typedef pair<int,int> PII; const int N=131072; int n; int s[N],a[N]; int cur,vis[N]; priority_queue<PII> qs,qb; int res; inline int rd(void) { int x=0,f=1; char c=getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=-1; for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(void) { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); n=rd(); rep(i,1,n) s[i]=rd(); rep(i,1,n) a[i]=rd(); rep(i,1,n) qb.push(mp(2*s[i]+a[i],i)); PII t1,t2; int e1,e2,cs; rep(i,1,n) { while (!qs.empty()) { t1=qs.top(); if (vis[t1.y]) qs.pop(); else break; } while (!qb.empty()) { t2=qb.top(); if (t2.y<cur||vis[t2.y]) qb.pop(); else break; } e1=(!qs.empty()); e2=(!qb.empty()); if (!e1&&e2) cs=2; else if (e1&&!e2) cs=1; else if (e1&&e2) { t1=qs.top(),t2=qb.top(); if (t2.x-2*s[cur]>=t1.x) cs=2; else cs=1; } if (cs==1) { t1=qs.top(); qs.pop(); vis[t1.y]=