牛客练习赛 16
在各位ak的大佬中,我感觉我寄几好菜啊。。。
题目描述
子序列:https://en.wikipedia.org/wiki/Subsequence
字典序:https://en.wikipedia.org/wiki/Lexicographical_order
输入描述:
一行一个字符串s (1 <= |s| <= 100,000)。
输出描述:
字典序最大的子序列。
输入
ababba
输出
bbba
输入
abbcbccacbbcbaaba
输出
cccccbba
A是IQIYI笔试题。。233 。我们让循环一开始的起始位置为$ 0 $。然后做$26$次循环,从'Z'到'A'。若当前循环的字符为$k$,我们从起始位置开始找所有出现的字符$k$,每出现一次就加到答案字符串末尾,然后更新起始位置为当前位置。这样构造出的一定是字典序最大的字符串。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
题目描述
定义这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)。i
第二行n个整数表示树的高度( 1 <= a
<= 100,000)。
输出描述:
输出一个整数表示最少修改树的高度的数目。
输入
3 2 2 2
输出
1
输入
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 $。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
题目描述
请问至少需要加多少个点,使得点对之间互相可以到达。
输入描述:
第一行一个整数n表示点数( 1 <= n <= 100)。i
第二行n行,每行两个整数x
, yi
表示坐标( 1 <= xi
, yi
<= 1000)。
y轴正方向为北,x轴正方形为东。
输出描述:
输出一个整数表示最少需要加的点的数目。
输入
2 2 1 1 2
输出
1
输入
2 2 1 4 1
输出
0
我们建个图,对于任意两个在同行或同列的点我们都连一条边。如果两点可达,那么这两个点一定在一个联通块里。因此我们拿并查集统计下有多少联通块。若有$k$个联通块,最少加$k-1$个点把这些联通块连起来全部可达了。因此答案为$k-1$。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
D.k进制数
比如说,在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。
输出描述:
输出一个整数表示幸运的子串数。
输入
10 5 6 3 2 0 5 6 1
输出
5
说明
3 2
3 2 0
0 5
5
2 0 5 6 1
是幸运的。
输入
7 6 4 3 5 0 4
输出
1
输入
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$的情况去掉就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
题目描述
定义f(l, r) = al | al+1| ... | ar。
现在枚举(1 <= l <= r <= n),问不同的f值一共有多少个。
输入描述:
第一行一个整数n表示数组大小 (1 <= n <= 100,000);i
第二行n个整数满足0 <= a
<= 1000,000。
输出描述:
输出一个整数表示不同的f值一共有多少个。
输入
3 1 2 0
输出
4
输入
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 $是排序的复杂度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
题目描述
输入描述:
第一行两个整数n,d(1 <= n <= 100,000,1 <= d <= 1000,000,000);i
第二行n个整数满足abs(a
) <= 1,000,000,000。数据保证a单调递增。
输出描述:
输出一个整数表示满足条件的选法。
输入
4 3 1 2 3 4
输出
4
输入
4 2 -3 -2 -1 0
输出
2
输入
5 19 1 10 20 30 50
输出
1
这也是一个水题。你先排序一下。当确定最大值为 $ a_j $时, 用lower_bound找找前面大于等于 $ a_j - d $的第一个数$ a_i $,因此我们可以在 $ [ i , j-1 ] $中任选两个数作为一个组合,对答案的贡献为$ C_{j-i}^2 $。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }