牛客练习赛16

字典序最大的子序列

题意:给定字符串S,求出字典序最小的子序列

很明显就是从前往后选, 每次选后缀最大的一个字符,这样一定最优

#include<stdio.h>
char s[1000000];
int main()
{
	scanf("%s",s);
	int i,j=0,k,last=0;
	for(k=127;k;k--)for(i=last;s[i];i++)if(s[i]==k)s[j++]=s[last=i];
    s[j]=0;
	puts(s);
}

漂亮的树

街上有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”不是。
现在请问最少修改几棵树的高度(可以变大也可以变小),使得这些树是漂亮的。

分析:有个关键点位要发现 如果a1定下来了 整个序列就定下来了

也就是说 如果a1定下来 我们修改的次数也就知道了 但是不可能去枚举a1啊

正面想解决很难 反着想 如果a1定下来 修改的次数知道了 不用被修改的次数也就知道

这样我们可以统计 每个数如果不用修改 对所应的a1

答案就是 ans=max(ans,n-vis[a[i]])

这个题目巧妙之处在于 一个ai对应不同的a1 修改与否是不同的 但是一个ai一定只会对应一个不会被修改的a1

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100009;
int n,a[maxn],vis[maxn],ans=1e9;
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        cin >> a[i];
        if( i<=n/2 ) vis[ a[i]-i+1 ]++;
        else    vis[ a[i]-(n-i) ]++;
    }
    for(int i=1;i<=100000;i++)
        ans = min( ans,n-vis[i] );
    cout << ans;
}

任意点

平面上有若干个点,从每个点出发,你可以往东南西北任意方向走,直到碰到另一个点,然后才可以改变方向。

请问至少需要加多少个点,使得点对之间互相可以到达

分析:容易想到两个点 如果横纵坐标分别不同 那么只需要添加一个点就可以使得连通

如果两个点横纵坐标至少有一个是连通的 那么这两个点已经连通

考虑用并查集 将开始已经联通的点放入一个集合当中 最后统计有多少个集合cnt

答案就是cnt-1

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=105;
int n;
int fa[maxn]; 
struct node{
	int x,y;
}a[maxn];
int find(int xx){
	if(xx!=fa[xx])return fa[xx]=find(fa[xx]);
	return xx;
}
bool pd(int ii,int jj){
	if(a[ii].x==a[jj].x||a[ii].y==a[jj].y)return true;
	return false;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y,fa[i]=i;
	for(int i=1;i<n;i++)
	for(int j=i+1;j<=n;j++)
	if(pd(i,j))fa[find(i)]=find(j);
	int cnt=0;
	for(int i=1;i<=n;i++)
	if(find(i)==i)cnt++;
	cout<<cnt-1<<endl;
     return 0;
}

求值

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

分析:一个关键点位 从R出发向前最多只会有20个不同的值 最差的情况就是每次只会一个数位上多一个1

所以每次只需要依次暴力放进set里面判断就好了 滚动用两个set 一个记录 1~i-1 ,2~i-1 ,3~i-1 ...产生的不同的值

一个记录加入当前i 也就是1~i ,2~i ,3~i , ......

一个非常巧妙的暴力

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
set<int> st[2];
bool vis[N];
int x, n, last, ans;
int main() {
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        cin >> x;
        last = 1 - last;
        st[last].clear();
        set<int> :: iterator it = st[1-last].begin();
        for(; it != st[1-last].end(); ++ it) {
            int y = (*it)|x;
            vis[y] = true;
            st[last].insert(y);
        }
        st[last].insert(x);
        vis[x] = true;
    }
    for(int i = 0; i < N; i ++) if(vis[i]) ans++;
    cout << ans << endl;
    return 0;
}

选值

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

分析直接尺取法就好

#include<stdio.h>
int a[100100];
int main()
{
	int n,d;
	scanf("%d%d",&n,&d);
	for(int i=0;i<n;i++)
	{
		
		scanf("%d",&a[i]);
	}
	long long num=0;
	int j=0;
	for(int i=0;i<n-2;i++)
	{
		while(j<n&&a[j]-a[i]<=d)
		j++;

		num+=(long long)(j-i-2)*(j-i-1)/2;

	}
	printf("%lld\n",num);
}
posted @ 2022-08-09 15:32  wzx_believer  阅读(55)  评论(0编辑  收藏  举报