2021年8月26日模拟赛题解
题目大意
编程求出所有的 位数中,有多少个数中有偶数个数字 。
说明:
- 一位数有 个:。
- 不是一位数。
- 为什么不是一位数?原因参考于《九年义务教育六年制小学数学第八册教师教学用书》。
- 包含偶数个 可以为包含 个 。
解题思路
一道显然的数论题。
首先设 为 位数中有奇数个数字 的数的个数,设 为 位数中有偶数个数字 的数的个数。
显然我们可以从前一个状态推出当前的状态。
即在前一个状态的后面加上一个数。
即如果在原来有奇数个 的数后加上 就是当前的偶数个数字 的数,方案数为 。
如果在原来有偶数个 的数后加上 就是当前的奇数个数字 的数,方案数为 。
如果在原来有奇数个 的数后加上不为 的数字就是当前的奇数个数字 的数,方案数为 。
如果在原来有偶数个 的数后加上不为 的数字就是当前的偶数个数字 的数,方案数为 。
则有转移方程:
AC CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
long long n;
long long f[100005], a[100005];
long long mod = 12345;
signed main()
{
scanf("%lld", &n);
f[1] = 1;
a[1] = 8;
f[2] = 17;
a[2] = 73;
for(int i = 3; i <= n; ++i)
{
f[i] = f[i - 1] % mod * 9 % mod + a[i - 1] % mod;
f[i] %= mod;
a[i] = a[i - 1] % mod * 9 % mod + f[i - 1] % mod;
a[i] %= mod;
}
printf("%lld", a[n] % 12345);
return 0;
}
题目大意
有 件商品,每一件商品价格为 个单位。
现在你手中共有 个单位的现金,以及 张优惠券。
你可以在购买某件商品时,使用至多一张优惠券,若如此做,该商品的价格会下降至 。
请问你至多能购买多少件商品。
解题思路
考虑贪心,先不考虑有没有优惠卷的情况肯定是从小到大买,这样一定会买的最多的。
按照这个思想,每一次买的时候就找最小的。
有两种情况:
- 使用了优惠券之后获得了一个最小值。
- 没有使用优惠券的最小值。
分别对 和 进行排序。
每次找最小的。
还要将用来优惠券的那些东西按照 放到堆里面,为以后撤销优惠券使用。
这就是反悔贪心。
AC CODE
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
#define int long long
const int maxn = 1e6 + 5;
struct node
{
int p, q;
bool operator < (const node& rhs) const
{
return p - q > rhs.p - rhs.q;
}
} a[maxn];
int n, k, ans;
long long m;
priority_queue<node> Q;
bool cmp(node a, node b)
{
if(a.q != b.q)
return a.q < b.q;
return a.p < b.p;
}
bool CMP(node a, node b)
{
return a.p < b.p;
}
signed main()
{
scanf("%lld%lld%lld", &n, &k, &m);
for (int i = 1; i <= n; i++)
scanf("%lld%lld", &a[i].p, &a[i].q);
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= k; i++)
{
m -= a[i].q;
Q.push(a[i]);
if (m < 0)
{
printf("%lld\n", i - 1);
return 0;
}
}
ans = k;
sort(a + k + 1, a + n + 1, CMP);
for (int i = k + 1; i <= n; i++)
{
int tp = Q.top().p, tq = Q.top().q;
if (a[i].p - a[i].q > tp - tq && a[i].q + tp - tq <= m)
{
m -= (a[i].q + tp - tq);
ans++, Q.pop(), Q.push(a[i]);
}
else if (a[i].p <= m)
ans++, m -= a[i].p;
}
printf("%lld\n", ans);
return 0;
}
题目大意
给出一有 个点的树,现在要拆除一些线,但是需要保证至少有 个点,满足每个点都可以和至少一个点联通。
解题思路
做法一
考虑树形 DP
。
显然,对于两点一线的情况的个数,应该越多越好。
假设如果是 对点(两点一线),且 ,那么只需要 条边。
否则,说明有多出的点,多出的点要连出一条边与两点一线联通,则需要 条边。
现在问题就转为求这样的点对有多少。
设 表示以 为根的子树中能够组成许多两点一线的最大点数,包含节点 。
设 为 。
当结点 与它的子结点 连边时,其余子结点都无法与结点 连边,并且结点 的子结点无法和结点 连边,所以 。
转移方程:
最后 就是上面所说的 了。
做法二
当然,有 DP
就有贪心,先思考这张图。
对于这张图,思考,我们要从上往下匹配,还是从下往上匹配。
显然是从下往上匹配,因为一个子节点往上只有一个父节点,但一个节点往下可能有多个子节点。
显然最优的情况一定是一条边匹配两个点,即两点一线。
然后从下往上匹配,每遇到两个没访问过的点,就相连,这就是贪心的思路。
但是有可能树的形状不能满足这样的匹配,于是剩下的点就只能用一条边匹配了。
AC CODE
树形 DP
#include <bits/stdc++.h>
using namespace std;
#define _ 100005
int T, n, k, ans;
int tot, head[_], to[_ << 1], nxt[_ << 1];
int f[_][2];
void add(int u, int v)
{
to[++tot] = v;
nxt[tot] = head[u];
head[u] = tot;
}
void dfs(int u, int fa)
{
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == fa) continue;
dfs(v, u);
f[u][0] += f[v][1];
}
f[u][1] = f[u][0];
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == fa) continue;
f[u][1] = max(f[u][1], f[u][0] - f[v][1] + f[v][0] + 1);
}
}
signed main()
{
scanf("%d", &T);
while(T--)
{
tot = 0;
memset(head, 0, sizeof head);
memset(f, 0, sizeof f);
scanf("%d%d", &n, &k);
for(int i = 1; i < n; ++i)
{
int a;
scanf("%d", &a);
add(i + 1, a);
add(a, i + 1);
}
dfs(1, 0);
int kkk = (k & 1);
k = k - kkk;
if(f[1][1] * 2 >= k) printf("%d\n", k / 2 + kkk);
else printf("%d\n", f[1][1] + (k - f[1][1] * 2) + kkk);
}
return 0;
}
贪心
#include<bits/stdc++.h>
using namespace std;
const int _ = 100005;
int tot, ans;
int head[_], nxt[_ << 1], to[_ << 1];
bool vis[_];
inline int read()
{
int X = 0, w = 1;
char ch = 0;
while(ch < '0' || ch > '9')
{
if(ch == '-') w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') X = (X << 3) + (X << 1) + ch - '0', ch = getchar();
return X * w;
}
inline void add(int x, int y)
{
nxt[++tot] = head[x];
head[x] = tot;
to[tot] = y;
}
inline void dfs(int x, int y)
{
for(int i = head[x]; i; i = nxt[i])
if(to[i] != y)
{
dfs(to[i], x);
if(!vis[x] && !vis[to[i]]) vis[x] = vis[to[i]] = 1, ans++;
}
}
signed main()
{
int T = read();
while(T--)
{
int n = read(), k = read();
tot = 0;
ans = 0;
memset(head, 0, sizeof(head));
memset(vis, 0,sizeof(vis));
for(int i = 1; i < n; i++)
{
int a = read();
add(a, i + 1);
add(i + 1, a);
}
dfs(1, 0);
if(ans * 2 >= k)
printf("%d\n",(k + 1) / 2);
else
printf("%d\n", ans + (k - ans * 2));
}
return 0;
}
题目大意
有一张地图,其中有 个点,且有 个传送门,可以从 点传送到 点(单向边),但通过某个传送门需要一些钥匙。每个房间也有一些钥匙。幸运的是,你用钥匙传送后,钥匙不会消失。
注意,你要先通过传送门,才能拿到这个传送门的终点的钥匙。
假设你身处 点,请你计算到处于 点的出口需要经过的最少的传送门数量。
如果你逃不出一个地图,请输出 "No Solution"。
解题思路
首先,因为通过每个传送门的代价都相同,为 ,所以不需要求最短路径,用普通的 BFS
就行了。
首先可以用一个变量 记录所有钥匙的状态,因为你是否拥有一种钥匙的状态,是 或 。
即如果你有种类 和种类 的钥匙。
则 为 。
即现在可以用一个变量表示你拥有的所有钥匙的状态,也可以表示某个传送门需要某些钥匙的状态,总而言之,这种用一个变量表示原先用一个数组表示的东西,叫状态压缩。
然后跑一边 BFS
,可以通过一个传送门的条件是 opt&w==w( 为你当前拥有的钥匙的状态, 为通过当前传送门所需的钥匙的状态, 的定义是有零为假否则为真,刚好符合判断所需的性质)。
如不明白,看这个例子。
你当前所拥有的的钥匙为 0 1 0 1 0 1
若通过传送门所需的钥匙为 1 1 0 1 0 1
两者相 为 0 1 0 1 0 1
,和 1 1 0 1 0 1
不相等,故不能通过。
若通过传送门所需的钥匙为 0 0 0 1 0 1
两者相 为 0 0 0 1 0 1
,和 0 0 0 1 0 1
相等,故能通过。
注意在 BFS
时, 数组要用二维,分别是当前的点和当前拥有的钥匙的状态。
说一个常识(然而我却不知道),BFS
一到终点是必然是最优的。
AC CODE
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
int n, m, tot, head[maxn], x;
int vis[maxn][1025];
int ans = 1e9;
int f[maxn];
struct edge
{
int to, nxt, w;
} e[maxn];
void add(int a, int b, int w)
{
e[++tot].to = b;
e[tot].nxt = head[a];
e[tot].w = w;
head[a] = tot;
}
struct abc
{
int a, b, c;
};
void Bfs()
{
queue<abc> q;
q.push({1, f[1], 0});
while (!q.empty())
{
abc ABC = q.front();
int tmp = ABC.a, opt = ABC.b, dis = ABC.c;
q.pop();
if(vis[tmp][dis]) continue;
vis[tmp][dis] = 1;
if(tmp == n)
{
printf("%d", dis);
exit(0);
}
for (int i = head[tmp]; i; i = e[i].nxt)
{
if ((e[i].w & opt) == e[i].w)
{
q.push({e[i].to, f[e[i].to] | opt, dis + 1});
}
}
}
printf("No Solution");
}
signed main()
{
scanf("%d%d%d", &n, &m, &x);
for(int i = 1; i <= n; ++i)
{
for(int j = 0; j < x; ++j)
{
int a;
scanf("%d", &a);
if(a)
f[i] += (1 << j);
}
}
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
int l = 0;
for(int j = 0; j < x; ++j)
{
int c;
scanf("%d", &c);
if(c)
l += (1 << j);
}
add(a, b, l);
}
Bfs();
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122127