• 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("
      ");
      } 
      
      

    • 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-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
      ",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("
      ");
      	}*/
      	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
      ",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
      ",ans);
      } 
      
  • 相关阅读:
    迭代器基础知识
    C语言I博客作业09
    第一周作业
    C语言1博客作业04
    C语言I博客作业08
    C语言博客作业05
    C语言I作业12—学期总结
    C语言I博客作业10
    C语言I博客作业06
    C语言I博客作业11
  • 原文地址:https://www.cnblogs.com/nowheretrix/p/8994903.html
Copyright © 2020-2023  润新知