牛客~~
C.消除整数
题目链接:C-消减整数_牛客小白月赛32(重现赛)@XuSihan (nowcoder.com)
思路:这个题目我们应该从给定的H是奇数还是偶数入手,除了1以外,所减的数必定为偶数。因此我们讨论如下:
当H为偶数时,为了能够成功减到0,那么必定是减了两次1;
当H为奇数时,为了使得减到0的次数最少,所以减了一次1;
在上面处理以后确保H为偶数,然后开始循环,倘若H能够被当前减数的2倍整除,那么就减去2倍的减数,否则减去当前的减数不变。(注意,如果这里的条件变为:H大于当前减数的2倍,那么久减去减数的2倍则会超时!!)
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T,i;
int H,num;
cin >> T;
while (T--)
{
num=0;
i=1;
cin >> H;
if(H%2==0)
{
H-=2;
num+=2;
}
else
{
H-=1;
num++;
}
while (H)
{
if(H%(i*2)==0)
{
num++;
i*=2;
H-=i;
}
else
{
H-=i;
num++;
}
}
cout << num << endl;
}
return 0;
}
E.春游
题目链接:春游 (nowcoder.com)
总共有五种情况:
- 全部坐二人船:(n+1)/2∗a
- 坐二人船多出来的1人和其中一个二人船坐三人船:(n−2)/2∗a+b
- 全部坐三人船:(n+2)/3∗b
- 多出来的1人和其中一个三人船坐两个二人船:(n−2)/3∗b+2∗a
- 多出来的2人坐二人船:n/3∗b+a
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int t;
LL n, a, b;
int main()
{
cin >> t;
while(t--)
{
LL ans1 = 0, ans2 = 0;
scanf("%lld%lld%lld", &n, &a, &b);
ans1 = min((n + 1) / 2 * a, (n - 2) / 2 * a + b);
ans2 = min((n + 2) / 3 * b, min((n - 2) / 3 * b + 2 * a, n / 3 * b + a));
printf("%lld\n", min(ans1, ans2));
}
return 0;
}
F.五连珠
题目链接:F-五连珠_牛客小白月赛32(重现赛)@XuSihan (nowcoder.com)
模拟,用pair数组存每个数在矩阵中的位置,用st数组存行、列、对角线的未划掉的数字个数,每次划完后对st数组检查判断即可。
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 10;
int t;
int a, b, c;
PII pa[30], pb[30];
int sta[12], stb[12];
void work(int x, int st[], PII p[])//sta pa
{
st[p[x].x]--, st[p[x].y + 5]--;
if(p[x].x == p[x].y) st[10]--;
if(p[x].x + p[x].y == 4) st[11]--;
}
bool check(int st[])
{
for(int i = 0; i < 12; i++)
if(!st[i])
return true;
return false;
}
int main()
{
cin >> t;
while(t--)
{
for(int i = 0; i < 12; i++) sta[i] = stb[i] = 5;
//录入数据,并存下各个数字在矩阵中的坐标
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
{
scanf("%d", &a);
pa[a] = {i, j};
}
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
{
scanf("%d", &b);
pb[b] = {i, j};
}
//按顺序划掉数字,注意在得到答案后,一定要把剩余的数字读完
bool fa = false, fb = false;
for(int i = 0; i < 25; i++)
{
scanf("%d", &c);
if(!fa && !fb)
{
work(c, sta, pa);
work(c, stb, pb);
fa = check(sta), fb = check(stb);
//cout << fa << ' ' << fb << endl;
if(fa && fb) puts("0");
else if(fa) puts("1");
else if(fb) puts("2");
}
}
}
return 0;
}
J.统计个数
题目链接:J-统计个数_牛客小白月赛32(重现赛)@XuSihan (nowcoder.com)
暴力枚举即可
直接暴力,线的数量会被算两次,三角的数量会被算6次
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
int t, n, m;
bool g[N][N];
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
cin >> t;
while(t--)
{
memset(g, false, sizeof g);
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
g[x][y] = true;
g[y][x] = true;
}
int p = 0, q = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
if(i == j) continue;
for(int k = 1; k <= n; k++)
{
if(i == k || j == k) continue;
if(g[j][i] && g[i][k])
{
q++;
if(g[j][k]) p++;
}
}
}
//cout << p << ' ' << q << endl;
if(!p) puts("0/1");
else
{
p = p / 6 * 3, q /= 2;
int G = gcd(p, q);
printf("%d/%d\n", p / G, q / G);
}
}
return 0;
}
CF~~
B.Long Long
最大数值总和即为所有数组元素的绝对值总和。为了使得操作次数最少,我们尽可能使得小于等于 0 的数处于一个连续的操作区间内。
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
for(int i = 0; i < n; i ++ ) cin >> a[i];
int cnt = 0; long long summ = 0;
int pre = -1;
for(int i = 0; i < n; i ++ )
{
summ += abs(a[i]);
if(a[i] > 0)
{
if(pre == -1) continue;
pre = -1;
} else if(pre == -1 && a[i] < 0) pre = i, cnt ++ ;
}
cout << summ << " " << cnt << endl;
}
int main()
{
int T;
cin >> T;
while(T--)
solve();
return 0;
}
ps.记得开longlong 的summ,错误的原因竟然是int。。。
D. Apple Tree
题意:有n个节点,n-1条边,1是根节点,每个节点最多两个子节点,其中有2个苹果在点上,他会进入子节点,如果他下面有两个子节点,那么会进入任意一个,问你q次,每次a,b表示两个苹果所在的节点,问最终两个苹果落点有多少种不同可能。
解析:如果点a到底部有x种方案,点b到底部有y种方案,那么总方案数就是x*y。而对于点u,他的方案数取决于子节点的分叉数,因此我们需要自下而上更新父节点的方案贡献值。
注意:题中输入是无向边,但是规定1是根节点,因此需要自己递归求一下每个点的子节点和父节点到底是谁。如果每次都暴力更新到根节点,那么会超时,因此我们还需要记录层数,从下一层一层向上,这样每个点就只会遍历一次。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
int n,mx,fa[N];//mx表示最大达到层数,fa表示该点的父节点是谁
vector<int> edge[N],son[N],levle[N];
bool st[N];//判断已经遍历过
ll w[N];//表示该点到底部有几种选择
void dfs(int u,int f,int lev)//当前点u,父亲f,层数lev
{
son[f].push_back(u);//加入子节点
fa[u]=f;//记录父节点
mx=max(mx,lev);//更新最大深度
st[u]=true,levle[lev].push_back(u);//该层存入该点
for(int i=0;i<edge[u].size();i++)
{
int j=edge[u][i];
if(!st[j]) dfs(j,u,lev+1);//防止回搜
}
}
void init()
{
for(int i=0;i<=mx;i++) levle[i].erase(levle[i].begin(),levle[i].end());
mx=0;
for(int i=0;i<=n;i++)
{
st[i]=false;
w[i]=0;
edge[i].erase(edge[i].begin(),edge[i].end());
son[i].erase(son[i].begin(),son[i].end());
}
}
void solve()
{
scanf("%d",&n);
init();
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs(1,0,1);
for(int i=1;i<=n;i++) if(!son[i].size()) w[i]=1;//没儿子就是最底层
for(int i=mx;i>=2;i--)//自下往上更新w
{
for(int j=0;j<levle[i].size();j++)
{
int u=levle[i][j];
int f=fa[u];//父节点
w[f]+=w[u];
}
}
int q;
scanf("%d",&q);
while(q--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%lld\n",w[a]*w[b]);
}
}
int main()
{
int t=1;
scanf("%d",&t);
while(t--) solve();
return 0;
}