B.序列的与和
这题乍一读有点吓人, 但看完数据范围以后可以迅速锁定用dfs
对于n个数, 每个数两种状态: 选 / 不选, 定好n个数的状态后判断是否刚好有k个1, 是的话ans ++
注意被选上的第一个数要直接赋值而不是&
另外, 注意题目要求非空子序列
#include <iostream>
using namespace std;
int n, k;
long long ans, q[30];
bool check(long long x)
{
int res = 0;
while(x)
{
if(x & 1) res ++;
x >>= 1;
}
return res == k;
}
void dfs(int u, long long w, bool f) //f表示是否有元素以及被选
{
if(u == n)
{
if(check(w) && f) ans ++;
return;
}
dfs(u + 1, w, f);
if(f)
{
dfs(u + 1, w & q[u], f);
}
else
{
dfs(u + 1, q[u], 1);
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i = 0; i < n; i ++)
{
scanf("%d",&q[i]);
}
dfs(0, 0, 0);
printf("%lld", ans);
return 0;
}
D.幂运算
颇具迷惑性的一题, 继上次郑大专场的F题之后, 看到这种题就下意识去快速幂取模, 结果稻花香里说丰年, 听取WA声一片
计算2^2^n, 本质上就是2^n个2相乘, 用while循环, 每次循环两两结合, n次循环后即为最终答案(记得取模)
#include <iostream>
using namespace std;
long long n, p;
int main()
{
long long ans = 2;
scanf("%lld%lld",&n,&p);
while(n --)
{
ans = ans * ans % p;
}
printf("%lld", ans);
return 0;
}
E.平均数
AA制是个好东西
既然要方差最小, 那么每两个数之间的差值就要缩到最小
如何在保证序列不下降的前提下让方差最小呢?
为了尽可能让方差最小, 我们可以将S平均分给n个人,此时方差取最小值0, 但由于大家只会出整数的钱, 当S%n != 0时,我们需要将S对n的余数再分给一部分人。保证方差最小的前提下, 这个余数可以再次平均分给x个人,每个人分到最小的正整数——一元钱, 考虑到序列不下降, 我们需要把这x个1从序列的最后往前放
代码实现如下:
#include <iostream>
using namespace std;
const int N = 1e6 + 20;
long long n, m, q[N];
int main()
{
scanf("%lld%lld",&n,&m);
if(m % n)
{
for(int i = 1; i <= n; i ++)
{
if(i <= n - m % n) printf("%lld ", m / n);
else printf("%lld ", m / n + 1);
}
}
else
{
for(int i = 1; i <= n; i ++)
{
printf("%lld ", m / n);
}
}
return 0;
}
J.异次元抓捕
这题我很迷, 建议直接看题解
#include <iostream>
using namespace std;
int main()
{
int t, k;
scanf("%d",&t);
while(t --)
{
scanf("%d",&k);
if(k - 1) puts("NO");
else puts("YES");
}
return 0;
}
K.奖励关
贪心
小小地打个表会发现, 我们并不需要操作3, 在每次打败boss后, 进行一次操作2将攻击力翻个倍, 然后进行操作1刚好又可以直接击败boss
综上, 我们只需要计算总操作次数/2即可知道最多可以击败几个boss, 注意, 由于第一次操作可以直接击败第一个boss, 而第二次操作要强化攻击力, 计算时需要(n + 1) / 2
#include <iostream>
using namespace std;
int main()
{
int t, n;
scanf("%d",&t);
while(t --)
{
scanf("%d",&n);
printf("%d\n", (n + 1) / 2);
}
return 0;
}
M.找孙子
建议把题目改成Find Grandson, 现在的名字有骂街的嫌疑
建图后遍历图的所有节点, 并将其孙子节点的个数相加即可
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 20;
int n, m, cnt;
int h[N], e[2 * N], ne[2 * N], w[N], idx;
void add(int x, int y)
{
e[idx] = y;
ne[idx] = h[x];
h[x] = idx ++;
w[x] ++; //x子节点的个数;
}
int check(int x)
{
int res = 0;
for(int i = h[x]; i != -1; i = ne[i])
{
res += w[e[i]];
}
return res;
}
int main()
{
int x, y;
scanf("%d%d",&n, &m);
memset(h, -1, sizeof(h));
while(m --)
{
scanf("%d%d",&x,&y);
add(x, y);
}
for(int i = 1; i <= n; i ++)
{
printf("%d ", check(i));
}
return 0;
}