2023牛客寒假算法基础集训营3个人补题 A B C D E F G I K
A
题意
小红拿到了一个数组,她每次操作可以选择一个偶数除以2,可以操作任意次(也可以不操作)。求最终数组所有元素之和的最小值。
思路
简单贪心,所有非负偶数不断除以二就完了。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
if (x % 2 == 0 && x > 0) {
while (x % 2 == 0) x /= 2;
}
sum += x;
}
cout << sum << "\n";
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
B
题意
小红希望用恰好n块矩形积木来搭建正方形,其中小红可以自由选择每块积木的大小,但必须是 1 ∗ k 1*k 1∗k的长和宽。其中 1 ≤ k ≤ ⌈ n 2 ⌉ 1\leq k \leq \lceil \frac{n}{2} \rceil 1≤k≤⌈2n⌉。小红想知道,自己最大可以搭建多大的正方形?请你帮小红计算正方形的边长。如果无法用恰好n块矩形拼成正方形,请输出-1。
思路
打表找规律,先想过二分是否能做,但是没有想到check函数怎么写,最终打表加手玩分析把公式推出来了。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
cin >> n;
if(n==2) {
cout<<"-1\n";
return;
}
cout << (int)sqrt(1.0 * n * ((n - 1) / 2 + 1)) << "\n";
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
C
题意
构造一个长度为n的排列,满足对于每个 a i a_i ai,有 2 ≤ ∣ a i − i ∣ ≤ 3 2\le |a_i-i|\le 3 2≤∣ai−i∣≤3。
思路
直接dfs爆搜,由于每次只会向下四步,所以可以保证不会超时。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
int vis[N];
bool f;
int d[]={-2,-3,2,3};
void dfs(int k) {
if(k==n+1) {
f=1;
return;
}
for(int i=0;i<4;i++) {
if(d[i]+k>0&&d[i]+k<=n&&vis[d[i]+k]==0) {
vis[d[i]+k]=k;
dfs(k+1);
if(f) return;
vis[d[i]+k]=0;
}
}
}
int res[N];
void solve() {
cin>>n;
dfs(1);
if(!f) {
cout<<-1<<"\n";
return;
}
for(int i=1;i<=n;i++) {
res[vis[i]]=i;
}
for(int i=1;i<=n;i++) {
cout<<vis[i]<<" ";
}
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
D
题意
给定一个正整数n,小红和小紫轮流操作,每次取n的一个因子x,使得n减去x。谁先将n减到0谁输。
小红先手操作,她想知道在双方足够聪明的情况下,谁会获得最终的胜利?
思路
简单博弈,可以发现1为必赢态,然后可以用这种方法逆推转移状态,最终发现应该是直接判断数字的奇偶性。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
cin>>n;
if(n==1) {
cout<<"yukari\n";
}
else if(n==2) {
cout<<"kou\n";
}
else {
if(n%2) cout<<"yukari\n";
else cout<<"kou\n";
}
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
E
题意
选择了两个点A和B(保证两点不重合),它们的坐标分别为 ( x A , y A ) (x_A,y_A) (xA,yA)和 ( x B , y B ) (x_B,y_B) (xB,yB)。小红希望你选择一个整点C,满足三角形ABC为以AB为斜边的等腰直角三角形。
思路
思路很简单,可以使用初中学过的三垂直推公式O(1)判断,唯一麻烦的地方在于输出一定要强转为int型,卡在这里卡了半天。。。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
int xa, ya, xb, yb;
cin >> xa >> ya >> xb >> yb;
double mx = (xa + xb) * 1.0 / 2, my = (ya + yb) * 1.0 / 2;
if (my - xa + mx == (int)(my - xa + mx) &&
mx - my + ya == (int)(mx - my + ya)) {
cout << (int)(mx - my + ya) << ' ' << (int)(my - xa + mx) << "\n";
} else if (my + xa - mx == (int)(my + xa - mx) &&
mx + my - ya == (int)(mx + my - ya)) {
cout << (int)(mx + my - ya) << ' ' << (int)(my + xa - mx) << "\n";
} else
cout << "No Answer!\n";
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
F
题意
输出42。
思路
输出42。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
void solve() {
string s;
cin>>s;
cout<<"42\n";
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
G
题意
给定一直表达式,符号自己填,问是否有让等式成立的表达式。
思路
由于符号只有三种,所以只需要dfs暴力枚举就可以,要注意的问题在于新定义的运算要求参与运算的两个数都是正整数(因为这个卡 了半天),另外使用快速幂的时候开long long也会爆,需要开__int128。当然,取模用类似哈希的思想应该也可以,但是有可能会被卡哈希。
代码
#include <bits/stdc++.h>
#define int __int128
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
string s;
int ans;
bool f;
vector<int> v;
vector<int> ss(20);
vector<int> q;
__int128 read(){
__int128 x=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
void print(__int128 x){
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar(x%10+'0');
}
int qpow(int a, int b, int m) {
int ans = 1, base = a;
while (b) {
if (b & 1) ans = ans * base % m;
base = base * base % m;
b >>= 1;
}
return ans % m;
}
void check() {
int res = q[0], num = 0;
int i = 1;
while (i < q.size()) {
num = q[i];
if (ss[i - 1] == 0) {
res = num + res;
}
if (ss[i - 1] == 1) {
res = res - num;
}
if (ss[i - 1] == 2) {
if (num == 0||res<=0) return;
res = qpow(res, res, num);
}
i++;
}
if (res == ans) f = 1;
if (f) {
int i = 0;
while (i < q.size()) {
print(q[i]);
if (i < q.size()-1) {
if (ss[i] == 0) {
cout << "+";
}
if (ss[i] == 1) {
cout << "-";
}
if (ss[i] == 2) {
cout << "#";
}
}
i++;
}
cout << "=" ;
print(ans);
cout<<"\n";
}
}
void dfs(int k) {
if (k == v.size()) {
check();
return;
}
for (int i = 0; i <= 2; i++) {
ss[k]=i;
dfs(k + 1);
if (f) return;
}
}
void solve() {
cin >> s;
int num = 0;
for (int i = 0; s[i]; i++) {
if (s[i] == '?')
q.push_back(num), num = 0, v.push_back(i);
else if (s[i] == '=') {
q.push_back(num);
num = 0;
} else {
num = num * 10 + s[i] - '0';
}
}
ans = num;
dfs(0);
if (!f) cout << -1 << "\n";
}
signed main() {
//IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
I
题意
定义“约和运算”:对一个不小于2的整数而言,求它的所有不等于它本身的约数之和进行的运算。
记一个整数n的“约和”为S(n)。例如,S(10)=1+2+5=8。 给一个正整数x,找到一个正整数n满足S(n)=x。
思路
被E卡太久了最后看榜去开K了,没有发现I很简单,我们可以分类讨论,当x为奇数的时候,因为有一个数是1,所以剩下的我们假设能由一个或两个质数组成,然后预处理 1 0 6 10^6 106内质数然后枚举就可以了,至于为什么,就是哥德巴赫猜想了();偶数的时候由于保证了x-1和x-3至少有一个为质数,当x-3为质数的时候 ( x − 1 ) / d o t 2 (x-1)/dot 2 (x−1)/dot2一定满足条件,x-1为质数的时候 ( x − 1 ) 2 (x-1)^2 (x−1)2一定满足条件。唯一的坑就是在小数值的时候x=7需要特判一下,因为存在偶质数数2,使得8满足条件。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define fi first
#define sc second
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int n;
int a[N];
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; j++) {
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
void solve() {
cin >> n;
int x=n;
if (x == 7)
cout << 8 << "\n";
else if (n % 2 == 0) {
if (n - 3 > 0 && !st[n - 3]) {
cout << (n-3)*2 << "\n";
} else if (n - 1 > 0 && !st[n - 1])
cout << (n - 1) * (n - 1) << "\n";
else
cout << -1 << "\n";
} else {
n--;
if(!st[n]) {
cout<<n*n<<"\n";
return;
}
for (int i = 0; i < cnt; i++) {
if (n - primes[i] > 0 && !st[n - primes[i]] &&
n - primes[i] != primes[i]) {
cout << primes[i] * (n - primes[i]) << "\n";
return;
}
}
cout << -1 << "\n";
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
st[1] = 1;
get_primes(N);
for (int i = 1; i <= t; i++) {
solve();
}
}
K
题意
给定指定质数,要求将其进行排列,使得前i( 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n)个数相乘的约束个数之和最大。
思路
有点绕,感觉就是找规律。然后差分前缀和求每个因数出现的次数,最后枚举再用推出的公式使用等比数列求解。
代码
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int,int>PII;
const int N = 2e5+10,mod=1e9+7;
int a[N],s[N];
int qmi(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
int get(int x)
{
return (x%mod+mod)%mod;
}
void solve()
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%lld",&x);
s[x+1]--,s[0]++;
}
for(int i=1;i<=2e5;i++) s[i]=s[i-1]+s[i];
int ans=0,res=1;
for(int i=1;i<=2e5;i++)
{
int q = (i+1)*qmi(i,mod-2)%mod;
ans=get(ans+res*q%mod*get(qmi(q,s[i])-1)%mod*qmi(get(q-1),mod-2)%mod);
res=res*qmi(q,s[i])%mod;
}
cout<<ans<<endl;
}
signed main()
{
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}