P1311 选择客栈 模拟 ( + st表)

模拟

题目链接:https://www.luogu.org/problem/show?pid=1311

又一道好玩的题目~~

放一下洛谷的图:

 

看完题后最直接的想法是先找颜色相同的客栈,

然后再找两两之间是否有小于等于s的客栈,

统计答案,

具体怎么实现呢?

一边输入我就能处理出颜色相同的客栈,

因为我要求每两个颜色相同的客栈之间的是否有小于等于p的客栈,

所以要顺序遍历所有颜色相同的客栈,

什么东西可以方便的查询他的上一个元素和下一个元素?

链表!(数组!)

pre[],h[]分别代表上一个元素和最后一个元素

大概是这样:

    pre[i] = h[x];
    h[x] = i;

(链表没有好好学QAQQQ)

假设我们已经找到了所有颜色相同的客栈,

然后,

要找颜色相同的客栈之间,

是否有消费小于等于p的客栈?

那我直接找两客栈之间的最小值,

怎么快速查询最小值?

st表!

Codes:

 1 struct st{
 2     void init(){
 3         for(int i = 1;(1 << i) <= n;++ i){
 4             for(int j = 1;j + (1 << i) - 1 <= n;++ j){
 5                 dp[j][i] = minn(dp[j][i - 1],dp[j + (1 << (i - 1))][i - 1]);
 6             }
 7         }
 8     }
 9     int que(int l,int r){
10         int k = log2(r - l + 1);
11         return minn(dp[l][k],dp[r - (1 << k) + 1][k]);
12     }
13 }st;

 

60分思路出来了

Codes:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 200000 + 5;
 8 bool bo[N];
 9 int k,s,h[N],ans,cnt,kind[N];
10 int n,nxt[N],pre[N],dp[N][25];
11 int minn(int x,int y){if(x < y) return x;return y;}
12 struct st{
13     void init(){
14         for(int i = 1;(1 << i) <= n;++ i){
15             for(int j = 1;j + (1 << i) - 1 <= n;++ j){
16                 dp[j][i] = minn(dp[j][i - 1],dp[j + (1 << (i - 1))][i - 1]);
17             }
18         }
19     }
20     int que(int l,int r){
21         int k = log2(r - l + 1);
22         return minn(dp[l][k],dp[r - (1 << k) + 1][k]);
23     }
24 }st;
25 int main(){
26     int cnt = 0,x;
27     scanf("%d%d%d",&n,&k,&s);
28     for(int i = 1;i <= n;++ i){
29         scanf("%d%d",&x,&dp[i][0]);
30         if(!bo[x]){
31             kind[++ cnt] = x;
32             bo[x] = 1;
33         }
34         pre[i] = h[x];
35         h[x] = i;
36     }
37     st.init();
38     for(int i = 1;i <= k;++ i){//60 T4个点
39         for(int j = h[kind[i]];j;j = pre[j]){
40             for(int x = pre[j];x;x = pre[x]){
41                 int xx = x,yy = j;
42                 int t = st.que(xx,yy);
43                 if(t <= s)
44                     ans ++;
45             }
46         }
47     }
48     cout << ans << '
';
49     return 0;
50 }

仔细想想,其实38行到40行的for是有重复部分的

时间就大大大大的浪费在了只跑到那些点却不记录的过程中,

来自57某大神的想法(orzorz):

如果我们已经知道这个位置的客栈和在他之前的客栈x之间

有小于等于p的客栈,

那么在x之前的客栈和这个客栈之间一定有可行方案,

所以开一个数组tot[]

    tot[k]:记录之前颜色为k的客栈出现的次数

一个数组sum[]

    sum[k]:记录之前已找到的“小于花费”的颜色为k的客栈出现次数

是不是有点绕?

记住tot和sum是不一样的不一样的不一样的!

但是在找到小于花费的客栈时,可以用tot来更新sum(上面已经解释过了),

然后考虑优化st表

我们实际上不需要知道两个客栈之间的最小值,

我只要知道最近的小于s的客栈在哪就好啦~

当然也可能是他自己,

这样100就拿到啦~

Codes:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 const int N = 200000 + 5;
 8 int k,s,h[N],ans,now;
 9 int sum[N],tot[N]; 
10 int n;
11 int main(){
12     int x,y;
13     scanf("%d%d%d",&n,&k,&s);
14     for(int i = 1;i <= n;++ i){
15         scanf("%d%d",&x,&y);
16         if(y <= s)
17             now = i;
18         if(now >= h[x])
19             sum[x] = tot[x];
20         h[x] = i;
21         tot[x] ++;
22         ans += sum[x];
23     }
24     cout << ans << '
';
25     return 0;
26 }
27 /*
28 tot[k]:记录之前颜色为k的客栈出现的次数;
29 sum[k]:记录之前“已找到小于花费”的颜色为k的客栈出现次数;
30 h[k]:当前颜色为k的客栈最后出现的位置;*/

挺好玩的2333

当时讲的超级仔细~~rjj好负责的

这个可以学习~

 

原文地址:https://www.cnblogs.com/Loizbq/p/7693694.html