zoukankan      html  css  js  c++  java
  • 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好负责的

    这个可以学习~

     

  • 相关阅读:
    【Linux】在Linux上,使用校园出校器拨号的一个脚本。
    【Android】编译CM10.1遇到的错误解决方案
    【Android】编译CM10遇到的错误解决方案
    【Android】CM在repo中使用local manifest
    一个网站的诞生 MagicDict开发总结1 [首页]
    我记录网站综合系统 1.6发布
    带有ToolTip的ListBox
    字符串的宽度
    .NET开发的文本编辑器,(又发明轮子了,VB代码,不喜误入)
    捕获输入内容
  • 原文地址:https://www.cnblogs.com/Loizbq/p/7693694.html
Copyright © 2011-2022 走看看