牛客周赛94
随手写一下题解吧,最后一题确实有点烧脑了,一开始没想到,看完题解确实茅塞顿开了
经典校招题
思路:n级台阶,每次只能走1或2格,问你最少得步数,那肯定就是每次都走两个,如果是奇数就多走一个,否则就是整除
#include<bits/stdc++.h>
using namespace std;
int n;
signed main()
{cin>>n;cout<<n/2+(n%2==1);
}
小苯购物
思路:纯暴力把每种情况枚举一遍即可
#include<bits/stdc++.h>
using namespace std;
#define int long longvoid solve() {int n;cin >> n;int a[4], b[4];for(int i = 1; i <= 3; i++) {cin >> a[i] >> b[i];}int ans = n; int order[3] = {1, 2, 3};do {int current = n;for(int i = 0; i < 3; i++) {int coupon = order[i];if(current >= a[coupon]) {current = max(current - b[coupon], 0LL);}}ans = min(ans, current);} while(next_permutation(order, order + 3));cout << ans << "\n";
}signed main() {ios::sync_with_stdio(false);cin.tie(0);int t;cin >> t;while(t--) {solve();}return 0;
}
小苯的与三角形
思路:这题其实很容易去联想位运算这个东西,众所周知,两个数的&一定会小于等于两个数之中最小的那一个,因此我们可以知道,如果y+x&y>x即可成立,那么也就是说我们的y&(y-1)都无法大于x的话,一定就是-1,否则的话,只要我们的x&y最小只需要是y的最大的那一位的1的位置,即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{int t;cin >> t; while (t--) {int x;cin >> x; if ((x & (x - 1)) == 0) {cout << -1 << endl;} else {cout << (1 << (31 - __builtin_clz(x))) << endl; }}return 0;
}
小苯的序列染色
思路:这题其实和c题差不多都是有点考验思维,我们仔细分析一下发现,每次只能涂成110,并且初始的时候,所有数的数值都是0,因此我们可以知道,我们可以先把整个序列最左边连续的0丢掉,比如说001100,我们就可以看成1100,然后我们知道,最后一位肯定不可能是1,然后嘞,剩下的序列,第二个数绝对不可能是0,我们直接去实现上述思路即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n;
string s;void solve() {cin >> n;cin >> s;if (s[n - 1] != '0') {cout << "NO" << endl;return;}int j = 0;while (j < n && s[j] == '0') {j++;}if (j + 1 < n && s[j + 1] == '0') {cout << "NO" << endl;return;}cout << "YES" << endl;
}signed main() {int T = 1;cin >> T;while (T--) {solve();}return 0;
}
小苯的数字操作
思路:这题怎么说呢?我们只有两种操作,乘二和除二,那么我们考虑那种情况更特殊呢?好吧,其实都很特殊,只有奇数乘二会出现过程中不会出现的东西,只有奇数除二才会导致丢失一个1
众所周知,我们只有k次操作机会,算上一开始的数,如果全是乘法操作的话,也就是说会有k+1种情况了现在,那么我们只需要去遍历k种操作或者说等n变成1被除成0的时候结束操作即可,我们只需要在n变成奇数的时候去加上剩余的次数了并且这个奇数不为1,是1直接加1即可,偶数就直接+1
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n,k;
void solve()
{cin>>n>>k;int ans=k+1;while(n&&k){if(n%2==1){if(n==1)ans+=1;elseans+=k;}else{ans+=1;}n/=2;k--;}cout<<ans<<"\n";
}
signed main()
{cin>>t;while(t--){solve();}return 0;
}
小苯的小球分组
题意:我们先来说一下题意吧,防止到时候绕迷糊了,就是说有n个小球,每个小球都有自己的颜色,然后给你一个定义f(X)表示对于X这个序列, 满足两个条件的最小分组,第一个是每个组内最多有俩球,第二个是每个组内的球的颜色不能相同,求出所有子序列的最小分组个数之和
思路:我们首先首先来考虑两种情况
第一种是,第一组有5个球,第二组有3个球,第三组有3个球,问你最少分多少组,那么我们从谈心的角度来考虑,尽可能多的将两个异色球放在一起,所以第一组和第一组先配对三个,然后剩下的就是第一组的两个和第三组的两个配对,最后剩下一个第三种颜色的球,我们发现这种就相当于球的总个数除二去向上取整
第二种是第一组有五个球,第二组和第三组各有两个球,俺们我们的配对方式就是第一组和第二组配对两个,第一组和第三组配对两个,我们就会发现,这样的分组数,其实就是最多的球的颜色出现的个数
但是我们现在想要求出来总个数,该怎么办呢?
我们能否假设,所有子序列都是满足第一种情况的,球的总个数/2向上取整,答案是肯定的,那么我们可以用组合数的公式去求我们假设情况下总共的个数应该为
(len+1)/2*C(n,len);我们的len表示的是当前序列的长度,len直接去遍历1到n即可
这样我们就找到了所有情况下的个数总和
那我们知道有些是假设的肯定不可能是真的啊,那部分该怎么去求呢?
我们在这里去引入一个绝对众数的概念
什么是绝对众数呢?就是说当前的我们当前的众数的出现次数,要超过其余元素的出现的次数的累加和
好了解释完这个概念我们继续往下讨论,我们会发现,其实对于绝对众数的这个概念,也就是我在上面列举的第二种情况,所以我们可以去考虑枚举绝对众数,我们用一个三层循环,最外层循环式遍历的颜色,第二层循环是遍历当前颜色出现的次数,我们将第二层变成我们子序列的绝对众数即可,第三层循环,我们去遍历其余元素出现的次数,其余元素的出现次数应当小于绝对众数并且小于等于剩下的元素个数
然后我们来考虑要减掉什么,加上什么
首先就是减掉的是我们假设的那部分为子序列长度/2向上取整的那部分可能出现的子序列次数为
(j+k+1)/2*C(cnt[i],j]*C*(n-cnt[i],k)即可
那么我们加上的是什么
(j+1)/2*C(cnt[i],j]*C*(n-cnt[i],k)即可
然后就是写代码了,思路确实有点小复杂,但是学完会有好处的
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define LL long long
int t;
int n;
int x;
int mod=998244353;
int f[5005];
int inv[5005];
int fast(int a,int b)
{int base=1;while(b){if(b%2==1){base=(base*a)%mod;}a=(a*a)%mod;b/=2;}return base;
}
int C(int n,int m)
{return (f[n]*inv[m])%mod*inv[n-m]%mod;
}
void solve(){cin>>n;unordered_map<int,int> mp;for(int i=1;i<=n;i++){cin>>x;mp[x]++;}int num=mp.size();vector<int> cnt;for(auto t:mp){cnt.push_back(t.second);}int ans=0;for(int i=1;i<=n;i++){ans=(ans+(i+1)/2*C(n,i)%mod)%mod;}for(int i=0;i<num;i++){for(int j=1;j<=cnt[i];j++){for (int k = 0; k < j && k <= (n - cnt[i]); k++){int len=k+j;int del=(len+1)/2*C(n-cnt[i],k)%mod*C(cnt[i],j)%mod;int add=j*C(n-cnt[i],k)%mod*C(cnt[i],j)%mod;ans=(ans-del+add+mod)%mod;}}}cout<<ans<<"\n";
}
signed main()
{int flag=1;f[0]=1;inv[0] = 1; for(int i=1;i<=5000;i++){flag=(flag*i)%mod;f[i]=flag;}inv[5000]=fast(f[5000],mod-2);for(int i=4999;i>=0;i--){inv[i]=inv[i+1]*(i+1)%mod;}cin>>t;while(t--)solve();return 0;
}