nowcoder-练习赛16
Posted 跟我一起这样做
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nowcoder-练习赛16相关的知识,希望对你有一定的参考价值。
A.字典序最大的子序列
给定字符串s,s只包含小写字母,请求出字典序最大的子序列。
两种做法。
1、从‘z‘开始搜索到‘a‘,从当前位置开始搜索此字母,把搜索到的添加到构造出的子序列的末端,然后从本次搜索到的字母的最后一个位置继续开始搜。直到搜完位置。
2、将整个字符串倒序,从头开始构造一个上升速度最快的不下降子序列即可。
#include"stdio.h" #include"string.h" char s[105000]; char a[105000]; int main(){ int i,l,x,now; scanf("%s",s); l=strlen(s); now=97; x=0; for (i=l-1;i>=0;i--){ if (s[i]>=now) { a[x]=s[i]; x++; now=s[i]; } } for (i=x-1;i>=0;i--) printf("%c",a[i]); printf("\n"); }
B.漂亮的树
街上有n棵树,标号为1...n,第i棵树的高度为ai。定义这n棵树是漂亮的,当且仅当
1. 对于所有的i,ai=a(n-i+1);
2. 对于1 <= i < n / 2 (不是整除),ai + 1= ai + 1。
由于本题树高的限制,极限情况不超过10万。为了使树高为正,我们在所有的树上都加10万,即所有的树高不超过20万,且大于0。在这之后,我们可以发现,对于每一颗树,它们都唯一属于一个分类,即当第1颗树为k时,这颗树是漂亮的。所以将每一颗数分类,然后找到最大的类即可。答案为n-最大的类的size。
#include"stdio.h" #include"string.h" #include"vector" using namespace std; int v[505000]; int a[505000]; int main(){ int n,i; scanf("%d",&n); for (i=1;i<=n;i++) {scanf("%d",&a[i]);a[i]+=100000;} memset(v,0,sizeof(v)); for (i=1;i<=(n+1)/2;i++) { if (a[i]-i+1>0) v[a[i]-i+1]++; } for (i=(n+1)/2+1;i<=n;i++) { if (a[i]-(n-i)>0) v[a[i]-(n-i)]++; } int ans=0; for (i=1;i<=500000;i++) if (v[i]>ans) ans=v[i]; printf("%d\n",n-ans); }
C.任意点
平面上有若干个点,从每个点出发,你可以往东南西北任意方向走,直到碰到另一个点,然后才可以改变方向。请问至少需要加多少个点,使得点对之间互相可以到达。
很厉害的题啊,完全没有想到是并查集。
我们将所给的点分类,分成一块一块的。块内的任意两个点均可达。可以发现,若两个点的横坐标或者纵坐标相同,那么它们一定是一个块的。然后,找到块的个数。此时,我们添加一个点,就可使两个块联通。当然,添加一个点,也最多可使两个点联通。ans=块的个数-1。
#include"stdio.h" #include"string.h" int x[105]; int y[105]; int vis[105]; int father[105]; int getfather(int k){ if (k==father[k]) return k; else father[k]= getfather(father[k]); return father[k]; } int main(){ int n,i,j,a,b,ans; ans=0; scanf("%d",&n); for (i=1;i<=n;i++) father[i]=i; for (i=1;i<=n;i++) scanf("%d %d",&x[i],&y[i]); for (i=1;i<=n;i++) for (j=i+1;j<=n;j++) if (x[i]==x[j]||y[i]==y[j]) { a=getfather(i); b=getfather(j); father[b]=a; } for (i=1;i<=n;i++) father[i]=getfather(father[i]); memset(vis,0,sizeof(vis)); for (i=1;i<=n;i++) if (vis[father[i]]==0){ vis[father[i]]=1; ans++; } printf("%d\n",ans-1); }
E.求值
给定n个数字a1, a2, ..., an。定义f(l, r) = al | al+1| ... | ar。现在枚举(1 <= l <= r <= n),问不同的f值一共有多少个。
呃。。讲讲我tle的做法。
首先线段树处理完从[l,r]的或和。处理完每个位置的倍增。然后从1到n枚举每一个位置。从当前位置开始尝试倍增最大的量,如果倍增完之后到达的位置的或和不变,则直接跳过去。否则,二分跳的长度,也就是先跳一半的距离,继续尝试到达那个点的或和是不是不变。若不变,则跳,否则继续迭代。直到,往前跳一个位置。复杂度约为O(n(logn)^2)。然后光荣的T了。
正确的做法如下。
由于数不超过2^20。也就是说,在每一个位置往后跳的次数,最多不过20次。所以我们只需要预处理在当前位置的或的每一位,在哪些点最先会被改变。处理出这个之后,按点的先后排序,然后从最先出现的点开始或,或好了一个位置,加入到set中。最后输出set的size。
那么现在问题在于,如何快速预处理出,在当前位置的或的每一位,在哪些点最先会被改变。从后往前递推即可。若为1,跟新,反之,从后面获得。
#include"stdio.h" #include"string.h" #include"set" #include"algorithm" #define maxn 105000 using namespace std; set<int>s; int a[maxn][21]; int b[maxn]; int sign[50]; struct node{ int wei,sum; }xu[50]; int cmp1(node a,node b){ return a.sum<b.sum; } int main(){ int i,n,x,now,l,j; int ff=0; scanf("%d",&n); for (i=1;i<=n;i++) scanf("%d",&b[i]); memset(a,-1,sizeof(a)); for (i=n;i>=1;i--){ l=0; x=b[i]; if (b[i]==0) ff=1; memset(sign,0,sizeof(sign)); while(x>0){ l++; sign[l]=x%2; x=x/2; } for (j=1;j<=20;j++) if (sign[j]==1) a[i][j]=i; else a[i][j]=a[i+1][j]; } s.clear();/* for (i=1;i<=n;i++){ for (j=1;j<=20;j++) printf("%5d",a[i][j]); printf("\n"); }*/ for(i=1;i<=n;i++) { now=b[i]; for (j=1;j<=20;j++) { xu[j].wei=j; xu[j].sum=a[i][j]; } sort(xu+1,xu+1+20,cmp1); xu[21].sum=-1; for (j=1;j<=20;j++) if (xu[j].sum!=-1) { if (xu[j].sum!=xu[j+1].sum){ now=now|(1<<(xu[j].wei-1)); s.insert(now); }else { now=now|(1<<(xu[j].wei-1)); } } } printf("%d\n",s.size()+ff); }
F.选值
给定n个数,从中选出三个数,使得最大的那个减最小的那个的值小于等于d,问有多少种选法。
尺取法即可。
#include"stdio.h" long long a[105000]; int main(){ long long n,m,i; scanf("%lld %lld",&n,&m); for (i=1;i<=n;i++) scanf("%lld",&a[i]); long long r=1; long long ans=0; for (i=1;i<=n;i++){ while(r<=n&&a[r]-a[i]<=m) r++; if (r-1-i>=2) ans+=(r-1-i)*(r-1-i-1)/2; } printf("%lld\n",ans); }
以上是关于nowcoder-练习赛16的主要内容,如果未能解决你的问题,请参考以下文章
Nowcoder 练习赛26 D xor序列 ( 线性基 )
降智严重——nowcoder练习赛46&&codeforces #561 Div2
nowcoder 1.20牛客练习赛95 ADuplicate Strings