zoukankan      html  css  js  c++  java
  • 牛客练习赛 16

    在各位ak的大佬中,我感觉我寄几好菜啊。。。

    A.字典序最大的子序列

    题目描述

    给定字符串s,s只包含小写字母,请求出字典序最大的子序列。
    子序列:https://en.wikipedia.org/wiki/Subsequence
    字典序:https://en.wikipedia.org/wiki/Lexicographical_order

    输入描述:

    一行一个字符串s (1 <= |s| <= 100,000)。

    输出描述:

    字典序最大的子序列。
    示例1

    输入

    ababba

    输出

    bbba
    示例2

    输入

    abbcbccacbbcbaaba

    输出

    cccccbba

    A是IQIYI笔试题。。233 。我们让循环一开始的起始位置为$ 0 $。然后做$26$次循环,从'Z'到'A'。若当前循环的字符为$k$,我们从起始位置开始找所有出现的字符$k$,每出现一次就加到答案字符串末尾,然后更新起始位置为当前位置。这样构造出的一定是字典序最大的字符串。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 using namespace std;
     8 string s,ans;
     9 int pos,p;
    10 int main()
    11 {
    12     ios::sync_with_stdio(false);
    13     cin>>s;
    14     pos=0;
    15     for(char p='z';p>='a';p--)
    16     {
    17         for(int i=pos;s[i];i++)
    18         {
    19             if(s[i]==p)
    20             {
    21                 ans+=p;
    22                 pos=i;
    23             }
    24         }
    25     }
    26     cout<<ans<<endl;
    27     return 0;
    28 }
    View Code

    B.漂亮的树

    题目描述

    街上有n棵树,标号为1...n,第i棵树的高度为ai
    定义这n棵树是漂亮的,当且仅当
        1. 对于所有的i,ai=an-i+1
        2. 对于1 <= i < n / 2 (不是整除),ai + 1= ai + 1;
    比如说 “2 3 4 5 5 4 3 2”和“1 2 3 2 1”是漂亮的而“1 3 3 1”和“1 2 3 1”不是。
    现在请问最少修改几棵树的高度(可以变大也可以变小),使得这些树是漂亮的。

    输入描述:

    第一行一个整数n表示树的数量( 1 <= n <= 100,000)。
    第二行n个整数表示树的高度( 1 <= a
    i
    <= 100,000)。

    输出描述:

    输出一个整数表示最少修改树的高度的数目。
    示例1

    输入

    3
    2 2 2

    输出

    1
    示例2

    输入

    4
    1 2 2 1

    输出

    0



    我们先考虑前$ frac{n+1}{2} $ 的数字,由于$a_i-a_{i-1}=1 $,所以必须调成递增的差值为$1$的递增序列。我们最朴素的想法是先确定$a_1$的值,对于不同的$ a_1=k $我们算有多少个$a_i =k+i $,找最大的那个。这样就把$ a_i $分成了几个集合。但是这样枚举$k$想想会超时。但是这时你会惊奇的发现,对于在一个集合里的元素$ a_i-i  $是相同的。因此我们统计一下对于每个值$a_i-i $的数量。对于后半段的数字也是类似的操作。然后我们找这些数量的最大值$maxn$。$n-maxn$就是答案。鉴于可能出现负数,做桶排的时候下标要在加个$ P=1000000 $。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 using namespace std;
     8 const int N=1e5+10;
     9 const int P=1e5;
    10 int n,m;
    11 int ans;
    12 int a[N];
    13 int high[N*2];
    14 int main()
    15 {
    16     scanf("%d",&n);
    17     for(int i=1;i<=n;i++)
    18         scanf("%d",a+i);
    19     m=(n+1)/2;
    20     for(int i=1;i<=m;i++)
    21     {
    22         high[P+a[i]-i]++;
    23     }
    24     for(int i=m+1;i<=n;i++)
    25     {
    26         high[P+a[i]-(n-i+1)]++;
    27     }
    28     ans=0;
    29     for(int i=1;i<=2*P;i++)
    30         ans=max(ans,high[i]);
    31     printf("%d
    ",n-ans);
    32     return 0;
    33 }
    View Code

    C.任意点

    题目描述

    平面上有若干个点,从每个点出发,你可以往东南西北任意方向走,直到碰到另一个点,然后才可以改变方向。
    请问至少需要加多少个点,使得点对之间互相可以到达。

    输入描述:

    第一行一个整数n表示点数( 1 <= n <= 100)。
    第二行n行,每行两个整数x
    i
    , y
    i
    表示坐标( 1 <= x
    i
    , y
    <= 1000)。
    y轴正方向为北,x轴正方形为东。

    输出描述:

    输出一个整数表示最少需要加的点的数目。
    示例1

    输入

    2
    2 1
    1 2

    输出

    1
    示例2

    输入

    2
    2 1
    4 1

    输出

    0

    我们建个图,对于任意两个在同行或同列的点我们都连一条边。如果两点可达,那么这两个点一定在一个联通块里。因此我们拿并查集统计下有多少联通块。若有$k$个联通块,最少加$k-1$个点把这些联通块连起来全部可达了。因此答案为$k-1$。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 using namespace std;
     8 const int N=1e2+10;
     9 int fa[N],ans;
    10 int x[N],y[N];
    11 int n,m;
    12 int Find(int x)
    13 {
    14     if(fa[x]!=x)
    15         fa[x]=Find(fa[x]);
    16     return fa[x];
    17 }
    18 void Union(int u,int v)
    19 {
    20     fa[Find(u)]=Find(v);
    21     return ;
    22 }
    23 void init(int n)
    24 {
    25     for(int i=1;i<=n;i++)
    26         fa[i]=i;
    27 }
    28 int main()
    29 {
    30     scanf("%d",&n);
    31     init(n);
    32     for(int i=1;i<=n;i++)
    33         scanf("%d%d",x+i,y+i);
    34     for(int i=1;i<=n;i++)
    35         for(int j=1+1;j<=n;j++)
    36         {
    37             if(x[i]==x[j])
    38             {
    39                 if(Find(i)!=Find(j))
    40                     Union(i,j);
    41             }
    42             if(y[i]==y[j])
    43             {
    44                 if(Find(i)!=Find(j))
    45                     Union(i,j);
    46             }
    47         }
    48     ans=0;
    49     for(int i=1;i<=n;i++)
    50         if(fa[i]==i)
    51             ans++;
    52     ans--;
    53     printf("%d
    ",ans);
    54     return 0;
    55 }
    View Code

    D.k进制数

    对于k进制数x,定义d(x)为x的各数位的和的k进制表示,如果结果超过一位,则继续重复执行各数位求和操作,直至结果为1位。
    比如说,在7进制下,d(35047)=d((3+5+0+4)7)=d(157)=d((1+5)7)=d(67)=6
    定义x为幸运的,当且仅当d(x) = b;
    现在给定k进制下的n个数位a1 a2 an,问其中有多少子串组成的数字是幸运的。

    输入描述:

    第一行三个整数k,b,n(2 <= k <= 1,000,000,000, 0 <= b < k, 1 <= n <= 100,000)。
    第二行n个整数满足0 <= a

    i

     < k。

    输出描述:

    输出一个整数表示幸运的子串数。

    示例1

    输入

    10 5 6
    3 2 0 5 6 1

    输出

    5

    说明

    3 2
    3 2 0
    0 5
    5
    2 0 5 6 1
    是幸运的。
    示例2

    输入

    7 6 4
    3 5 0 4

    输出

    1
    示例3

    输入

    257 0 3
    0 0 256

    输出

    3



    结论题。。反正我不知道结论是怎么的出来的,等明天看题解来获悉结论的证明。

    首先对于最后剩下的数,假设原来数字为$p$,在不断按位求和中,每个和的结果$ p_i  mod (k-1) $ 皆为同一个数。

    先考虑$b=0$的情况,因为$0$的情况和$k-1$的情况得出来的是同一个余数0。但两者明显不同,因此我们分开算,$0$为一部分,$k-1$和其他数为一部分。

    求$b=0$的子串数,那么我们统计所有0连续出现最大区间。若区间[L,R]里全为0,那么在这个区间能得到$b=0$的子串有$ frac{(r-l+1)(r-l+2)}{2} $个。这个显而易见。那把所有$0$连续出现的最大区间的子串数相加就是$b=0$的答案。

    然后我们再算普通情况,这时候余数为0的情况即为余数为$k-1$的情况。

    我们先求个在$mod k-1$意义下的前缀和$pre[i]$。

    后面我们讨论均为在$mod k-1$的意义下。我们固定右端点$r$,然后找左端点l使得他们最后答案为$t=b mod  (k-1) $(此时k-1当0处理)。因此我们找$pre[l]$为$pre[r]-t$的数量,这样他们在模$k-1$的意义下差为$t$,也就是我们需要的答案。

    在$b mod (k-1)=0$的情况下这个方法会多算$b=0$的情况到$b=(k-1)$中,因此把所有$b=0$的情况去掉就好了。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 using namespace std;
     8 const int N=1e5+10;
     9 LL a[N];
    10 LL b,k,zero,ans;
    11 int n,pre;
    12 map<LL,LL> tag;
    13 int main()
    14 {
    15     scanf("%d%d%d",&k,&b,&n);
    16     for(int i=1;i<=n;i++)
    17     {
    18         scanf("%d",a+i);
    19         if(a[i])
    20            pre=i;
    21         else
    22             zero+=i-pre;
    23         a[i]=(a[i-1]+a[i])%(k-1);
    24     }
    25     if(b==0)
    26     {
    27         printf("%lld
    ",zero);
    28         return 0;
    29     }
    30     k--;
    31     b%=k;
    32     for(int i=1;i<=n;i++)
    33     {
    34         tag[a[i-1]]++;
    35         ans+=tag[((a[i]-b)%k+k)%k];
    36     }
    37     if(b==0)
    38         ans-=zero;
    39     printf("%lld
    ",ans);
    40     return 0;
    41 }
    View Code

    E.求值

    题目描述

    给定n个数字a1, a2, ..., an
    定义f(l, r) = al | al+1| ... | ar
    现在枚举(1 <= l <= r <= n),问不同的f值一共有多少个。

    输入描述:

    第一行一个整数n表示数组大小 (1 <= n <= 100,000);
    第二行n个整数满足0 <= a
    i
     <= 1000,000。

    输出描述:

    输出一个整数表示不同的f值一共有多少个。
    示例1

    输入

    3
    1 2 0

    输出

    4
    示例2

    输入

    10
    1 2 3 4 5 6 1 2 9 10

    输出

    11

      E的话我们把数按二进制分成$20$位,因此我们现在有两维,一维是序列,一维是数位。我们先要计算一下在当前下标为$i$的位置,每个数位$k$最后一次出现的下标位置,这个可以递推解决。之后后我们做一下前缀或$pre[i]$ (跟前缀和差不多)。

      然后我们接下来固定区间右端点$r$,然后找不同的$l$的情况下会产生的数。这样的数最多$ 20 $个。一开始我们的数是$[1,r]$ 或后的结果,也就是$pre[r]$。我们前面算过下标为$r$,数位$k$出现的最晚位置,那么我们把这些位置和数位按照位置的前后顺序排序,然后把这些数位按前后顺序从$pre[r]$中从$1$变为$0$,这个排序+亦或解决。当然位置相同的必须同时变换。然后每次变换以后看看这个数字是否出现过,没有答案$+1$。因此我们还要写一个标记数组来确认数字是否出现过。

      对了还要特判一下$0$有没有在序列中出现过,有的话答案$ +1 $。

      因此综合一下复杂度差不多$ O(n 20 log_2 20)$。后面$ 20log_2 20 $是排序的复杂度。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 #define mp(x,y) make_pair(x,y)
     8 using namespace std;
     9 const int N=1e5+10;
    10 const int M=5e6+10;
    11 int a[N];
    12 int pre[N];
    13 int head[N][32];
    14 struct pa
    15 {
    16     int pre,bit;
    17 }st[32];
    18 bool vis[M];
    19 int n,m,k,p,tot;
    20 int ans;
    21 bool cmp(pa a,pa b)
    22 {
    23     return a.pre<b.pre;
    24 }
    25 int main()
    26 {
    27     scanf("%d",&n);
    28     ans=0;
    29     for(int i=1;i<=n;i++)
    30     {
    31         scanf("%d",a+i);
    32         if(a[i]==0)
    33         {
    34             vis[0]=1;
    35             ans++;
    36         }
    37         pre[i]=(pre[i-1]|a[i]);
    38         p=a[i];
    39         for(int k=0;k<32;k++)
    40         {
    41             if(p&1)
    42                 head[i][k]=i;
    43             else
    44                 head[i][k]=head[i-1][k];
    45             p>>=1;
    46         }
    47     }
    48     for(int i=1;i<=n;i++)
    49     {
    50         for(int k=0;k<32;k++)
    51             st[k]=(pa){head[i][k],k};
    52         sort(st,st+32,cmp);
    53         p=pre[i];
    54         for(int j=0;j<32;j++)
    55         {
    56             if(st[j].pre==0)
    57                 continue;
    58             if(j==0 || st[j].pre!=st[j-1].pre)
    59             {
    60                 if(!vis[p])
    61                 {
    62                     ans++;
    63                     vis[p]=1;
    64                 }
    65             }
    66             p^=(1<<st[j].bit);
    67         }
    68     }
    69     printf("%d
    ",ans);
    70     return 0;
    71 }
    View Code

    F.选值

    题目描述

    给定n个数,从中选出三个数,使得最大的那个减最小的那个的值小于等于d,问有多少种选法。

    输入描述:

    第一行两个整数n,d(1 <= n <= 100,000,1 <= d <= 1000,000,000);
    第二行n个整数满足abs(a
    i
    ) <= 1,000,000,000。数据保证a单调递增。

    输出描述:

    输出一个整数表示满足条件的选法。
    示例1

    输入

    4 3
    1 2 3 4

    输出

    4
    示例2

    输入

    4 2
    -3 -2 -1 0

    输出

    2
    示例3

    输入

    5 19
    1 10 20 30 50

    输出

    1

    
    

    这也是一个水题。你先排序一下。当确定最大值为 $ a_j $时, 用lower_bound找找前面大于等于 $ a_j - d $的第一个数$ a_i $,因此我们可以在 $ [ i , j-1 ] $中任选两个数作为一个组合,对答案的贡献为$ C_{j-i}^2 $。

     1 #include<bits/stdc++.h>
     2 #define clr(x) memset(x,0,sizeof(x))
     3 #define clr_1(x) memset(x,-1,sizeof(x))
     4 #define mod 1000000007
     5 #define LL long long
     6 #define INF 0x3f3f3f3f
     7 using namespace std;
     8 const int N=1e5+10;
     9 int n,d,p;
    10 int a[N];
    11 LL ans;
    12 int main()
    13 {
    14     scanf("%d%d",&n,&d);
    15     for(int i=1;i<=n;i++)
    16         scanf("%d",a+i);
    17     sort(a+1,a+n+1);
    18     ans=0;
    19     for(int i=3;i<=n;i++)
    20     {
    21         p=lower_bound(a+1,a+i,a[i]-d)-a;
    22         if(p<=i-2)
    23             ans+=(LL)(i-p)*(i-p-1)/2;
    24     }
    25     printf("%lld
    ",ans);
    26     return 0;
    27 }
    View Code
  • 相关阅读:
    深入理解volatile
    定时任务分布式锁的简单实现
    spring boot在tomcat运行多环境配置分离方案
    java spring boot data jpa和javaagent兼容问题
    RabbitMQ PHP操作类,守护进程及相关测试数据
    Python httpsqs封装类
    Python守护进程(多线程开发)
    python游戏打包exe
    处理谷歌地图marker旋转
    在vue-cli项目中mockjs和vConsole的使用
  • 原文地址:https://www.cnblogs.com/wujiechao/p/8965134.html
Copyright © 2011-2022 走看看