Noip2011 提高组 选择客栈
P1311 选择客栈
思路:
①看题,我们可以发现一个显然的性质,即当最左边的客栈向右移动时,最右边的客栈时单调向右的,并且右端点往右的客栈也符合要求。(因为只要左侧有一个满足的,右边的自然可以选)
不妨将每种颜色的宾馆分别放到 vector 中。
然后在每个 vector 中枚举左端点,维护一个单调指针来确定右端点 (vector中的下标)。
我们接下来就要快速判断一段区间是否合法,我们开一个ok数组,表示从i点开始最近的满足条件的位置(不考虑颜色)。
这样的话转移就是:
ok[i]=i;(p[i]<=P),反之:ok[i]=ok[i+1];
需要注意的是ok[n+1]=n+1;
表示如果p[n]>P则p[n]=p[n+1]=n+1,即不合法
②大模拟,详细看代码
坑点:
注意输入顺序qwq
上代码:
1)vector版
#include <iostream> #include <cstdio> #include <vector> using namespace std; const int K = 50; const int N = 200001; int n,k,p,ans; int w[N],ok[N]; vector<int>v[K]; void getnxt() { ok[n+1]=n+1; for(int i=n; i>=1; i--) { if(w[i]<=p) ok[i]=i; else ok[i]=ok[i+1]; } } int main() { scanf("%d%d%d",&n,&k,&p); for(int i=1,x; i<=n; i++) { scanf("%d%d",&x,&w[i]); v[x].push_back(i); } getnxt(); for(int i=0,d,s; i<k; i++) { d=0; s=v[i].size(); //i客栈连接的s个客栈 for(int j=0; j<s; j++) { d=max(d,j+1); //当前枚举到的最右端的客栈所在色调的编号是多少 while(ok[v[i][j]]>v[i][d] && d<s) { d++; } //这里不太懂... ans+=s-d; } } printf("%d",ans); return 0; }
2)纯模拟版
#include <iostream> #include <cstdio> using namespace std; const int K = 101; int n,k,p,ans; int precnt[K],ok[K],last[K]; //precnt[x] i点前颜色为x的客栈数 //ok[x] i点前满足能有价格<=p的客栈的颜色为x的客栈数 //last[x] i点 前一个颜色 为x的点的编号 int main() { scanf("%d%d%d",&n,&k,&p); for(int i=1,tmp,x,y; i<=n; i++) { scanf("%d%d",&x,&y); if(y<=p) tmp=i; //记录下来当前色调的可行的最右客栈编号 if(tmp>=last[x]) ok[x]=precnt[x]; ans+=ok[x]; precnt[x]++; //不管是不是可以满足条件 last[x]=i; } printf("%d",ans); return 0; }