编程语言
首页 > 编程语言> > 2021年中国大学生程序设计大赛(哈尔滨)Gym 103447

2021年中国大学生程序设计大赛(哈尔滨)Gym 103447

作者:互联网

B Magical Subsequence

E Power and Modulo

I Power and Modulo 二进制,思维,二分

题意
给你一个序列 \(A_n\),你可以进行多次操作;
每次操作可以选择一个任意长度的序列 \(B_m\) ,然后使得\(A_{B_i}-2^{i-1}\)。问你最少多少次操作可以将\(A_n\)序列变为零。

题解

可以发现
将A_n$ 分别求出来每个数的二进制位为 1 的数量

每次操作等价为消除下标从0开始的连续的1。
因此,问题等价为至少多少次操作将这些二进制的1消除掉。

问题也等价为:
将二进制存起来,从高位向低位变成一个不下降序列,此时最低位次数即所需次数
注意: 而为了使得操作次数最少,就要二进制数1的个数最少。

思路1:暴力,复杂度不明
从后往前遍历,如果发现当前位置大于前一个位置了,就匀到前一个位置些(匀出的个数*=2),以保证前一个位置个数 >= 当前位置个数。
进行若干遍此操作,便能保证低位到高位递减,同时 1 的个数最小化。

思路2:二分,复杂度明了
假设x次操作成立的话那么x+1次操作也一定成立,因此发现具有二段性可以二分。
和上面一样,我们从高位到低位枚举要将当前这一位的1全删掉需要的操作数(看看是否可以连续删即可),然后将当前这一位操作后剩下的给前面加上,再每一位都删除相应的数量即可,最后每一次的操作数累加起来就是res。

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll ay[40];
int n;
bool check(ll x){
	if(ay[0]>x) return 0;
	ll a[40];
	for(int i=0;i<=31;i++){
		a[i]=ay[i];
	}
	a[32]=0;
	for(int i=0;i<=31;i++){
		if(i&&a[i]>a[i-1]||a[i]>x) return false;
		ll y = i?a[i-1]-a[i]:x-a[i];
		a[i+1]-=y/2;
		a[i]+=y/2*2;
	}
	return true;
}
ll find()
{
	ll l=0 , r=1e16;
    while (l < r)
    {
        ll mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}
int main()
{
	int _;scanf("%d",&_);
	while(_--){
		memset(ay,0,sizeof ay);
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			ll x;scanf("%lld",&x);
			for(int j=0;j<=31;j++){
				ay[j]+=( (x>>j)&1 );
			}
		}
		printf("%lld\n",find());
	}
	
	
	return 0;
}

原文1

J Local Minimum 水题

题意: 求出\(n×m\) 的方格里面有几个数字是:不仅是同行最小,而且是同列最小。

思路:
先找出同行最小的位置,再找出同列最小的位置,判断有几个位置两个都满足

#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define pii pair<int, int>
const int N =1010;
// const int M=1000000007;
int n,m;
// vector<int> ans;
int g[N][N];
int a[N],b[N];
void slove()
{
    cin>>n>>m;
    int ans=0;
    for(int i=0;i<n;i++){
        int cnt=0x3f3f3f3f;
        for(int j=0;j<m;j++){
            cin>>g[i][j];
            cnt=min(g[i][j],cnt);
        }
        a[i]=cnt;
    }
    for(int i=0;i<m;i++){
        int cnt=0x3f3f3f3f;
        for(int j=0;j<n;j++){
            cnt=min(g[j][i],cnt);
        }
        b[i]=cnt;
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
          if(g[i][j]<=min(a[i],b[j])){
             ans++;
          }   
        }
        
    }

    cout<<ans<<endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
        slove();
    return 0;
}

标签:return,int,Gym,long,2021,ay,操作,ll,103447
来源: https://www.cnblogs.com/kingwz/p/16691149.html