牛客練習賽 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棵樹的高度為a i
定義這n棵樹是漂亮的,當且僅當
    1. 對於所有的i,a i=a n-i+1
    2. 對於1 <= i < n / 2 (不是整除),a i + 1= a i + 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",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\n",ans);
54     return 0;
55 }
View Code

 

D.k進制數

對於k進制數x,定義d(x)為x的各數位的和的k進制表示,如果結果超過一位,則繼續重復執行各數位求和操作,直至結果為1位。
比如說,在7進制下,d(3504 7)=d((3+5+0+4) 7)=d(15 7)=d((1+5) 7)=d(6 7)=6
定義x為幸運的,當且僅當d(x) = b;
現在給定k進制下的n個數位a 1 a 2 a n,問其中有多少子串組成的數字是幸運的。

輸入描述:

第一行三個整數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\n",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\n",ans);
40     return 0;
41 }
View Code

 

 

E.求值

題目描述

給定n個數字a 1, a 2, ..., a n
定義f(l, r) = a l | a l+1| ... | a r
現在枚舉(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\n",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\n",ans);
26     return 0;
27 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM