2020 ICPC 沈阳站
C Mean Streets of Gadgetzan
Description
有 个命题, 个事件作为命题可能的条件。命题有以下四种:
- 表示确定为真
- 表示确定为假
- -> 表示:如果都为真,则 为真
- -> 表示:如果都为真,则为假
构造出个条件的真假使得这 个命题不互相矛盾,并输出个字符来表示个条件的真假;
如果构造不出来,即 个命题互相矛盾,则输出“conflict”
Solution
注意到对于后面两个类型的命题,只要存在一个 为假,则无论 取何值命题都为真。
初始将所有条件默认为,标记为 (为了区分原始赋的 和确定下来为 这两种情况)。
对于前面两个类型的命题,能够直接确定条件的真假,将值为 的条件加入队列 (注意至多进队一次);
对于后面两个类型的命题,用结构体 来记录信息, 为当前命题记录在结构体中的编号(结构体的下标),类型的 记录 作为 -> 前的一个 的命题编号, 中存三个信息,①是命题包含的 个数 (实际记录没有置为 的 数目,初始为 总数目),②是命题中 -> 后的 ,③是命题的类型 表示若 都为真则 为真, 表示若 都为真则 为假。
每次从队首取出一个元素 ,访问 所有的值,将命题对应的 减一,若 ,则其对应的 值可以确定下来,若 第一次被置为 ,则加入队列。
在操作的过程中出现任何矛盾都直接输出 并结束程序。
注:说明中的实现和程序并不完全一样,但意义和逻辑相同。
Code
//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
using namespace std;
const int N = 1e6 + 5;
int n, m, col[N], anum, a[N][2];
bool atype[N], vis[N];
vector<int> ind[N];
queue<int>q;
string s;
void END() {
printf("conflict\n");
exit(0);
}
int main() {
cin >> n >> m;
getchar();
for (int i = 1;i <= m;++i) col[i] = -1;
for (int i = 1;i <= n;++i) {
getline(cin, s); //整行输入(不包括末尾换行符)
if (s.find("->") == -1) {
if (s[0] != '!') {
int x = stoi(s);
if (col[x] != -1) END();
col[x] = 1;
if(!vis[x]) q.push(x), vis[x]=1;
}
else {
int x = stoi(s.substr(1));
if (col[x] != -1) END();
col[x] = 0;
}
}
else {
int pos = s.find("->"), anum_tmp = 0;
++anum;
string tmps = "";
for (int j = 0;j < pos;++j)
if (s[j] != ' ') tmps += s[j];
else ind[stoi(tmps)].push_back(anum), tmps = "", ++anum_tmp;
atype[anum] = (s[pos + 3] != '!');
int x = stoi(s.substr(pos + 3 + (s[pos + 3] == '!')));
a[anum][0] = x;
a[anum][1] = anum_tmp;
}
}
while (q.size()) {
int u = q.front();q.pop();
for (int i = 0;i < ind[u].size();++i) {
int t = ind[u][i], x = a[t][0];
--a[t][1];
if (!a[t][1]) {
if (atype[t]) {
if (col[x] == 0) END();
col[x] = 1;
if (!vis[x]) q.push(x), vis[x] = 1;
}
else {
if(col[x] == 1) END();
col[x] = 0;
}
}
}
}
for (int i = 1;i <= m;++i)
if (col[i] == -1 || col[i] == 0) printf("F");
else printf("T");
return 0;
}
D-Journey to Un'Goro
Description
构造一个长度为 的序列,其组成元素只能是 和 ,区间 是一段“好”的区间当且仅当该区间内的 有奇数个。
首先输出长度为 的序列最多有多少个 “好” 的区间,然后按照字典序输出“好”的区间数最多的序列,若这样的序列超过100个,则只需输出按字典序排序后的前100个。
Solution
容易想到,当序列元素全为 时,达到最大答案:
若 为奇数,
若 为偶数,
由于最小只需要输出个序列,考虑搜索+剪枝。
尝试多找到一些性质来剪枝。
设表示序列的前个元素中的个数,则一个区间好,当且仅当为奇数 与的奇偶性不同。
若中共有个奇数,个偶数,则答案为,
当且仅当或时,取得最大值。
Code
//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
int n, totr[2], totbr[2], totans, maxr;
ll ans;
char seq[N];
void dfs(int cur, int totr1, int totr2, bool flag) {
//totr1 前缀r数目为奇数的个数 totr2 前缀r数目为偶数的个数 flag=0 当前前缀r数目为偶 flag=1 当前前缀r数目为奇
if (totr1 > maxr || totr2 > maxr) return;
if (cur == n) {
++totans;
seq[n] = '\0';
printf("%s\n", seq);
if (totans == 100) exit(0);
return;
}
seq[cur] = 'b';
dfs(cur + 1, totr1 + flag, totr2 + (flag ^ 1), flag); //注意(flag^1)处要打括号
seq[cur] = 'r';
dfs(cur + 1, totr1 + (flag ^ 1), totr2 + flag, flag ^ 1);
}
int main() {
cin >> n;
if (n % 2) ans = 1ll * (n + 1) * (n + 1) / 4;
else ans = 1ll * n * (n + 2) / 4;
cout << ans << endl;
maxr = (n + 2) / 2;
dfs(0, 0, 1, 0);
return 0;
}
F Kobolds and Catacombs 思维
Description
What's the maximum number of consecutive groups they can be partitioned into, such that after reordering the kobolds in each group in non-descending order, the entire queue is non-descending?
给定一个序列,问该序列最多能划分为多少个区间,满足:对每个区间内排序后,整个序列是不下降的。
Solution
思路:将序列排序,比较、观察原序列()与排序后序列()。
注意到对于下标 ,若 数组与 数组的前 个数相同, 就在 后面将序列划开。可以得到最大区间数目。
思考用什么特征值来判断前 个数是否相同。
可以证明 前缀和可以作为这个特征值。
简单证一下:
假设前缀和不足以作为该特征值,即 , 。
当时,
,
假设,则一定有 ,那么对 序列排序后, 之间一定还有一个$a_1 a_2$ 。则假设不成立。
的情况类似。
Code
//by DTTTTTTT
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6 + 5;
int n, a[N], b[N], ans;
ll suma[N], sumb[N];
int main() {
cin >> n;
for (int i = 1;i <= n;++i) cin >> a[i], b[i]=a[i];
sort(b + 1, b + n + 1);
for (int i = 1;i <= n;++i) {
suma[i] = suma[i - 1] + a[i];
sumb[i] = sumb[i - 1] + b[i];
if (suma[i] == sumb[i]) ++ans;
}
cout << ans << endl;
return 0;
}
G The Witchwood 签到题
Description
个物品,第 个物品价值 ,最多取 个物品,求最大价值。
Solution
签到题。 对所有 从大到小排序,取前 个加起来就是答案。
H The Boomsday Project DP+二分
Description
有 种优惠券,第 种优惠券的有效时间为 天,可以免费借用 次自行车,价格为 。
会借用自行车 天,在第 天,他会借用 次自行车。
如果不使用优惠券,单次借用自行车的价格为 元。
求 的最小费用。
Solution
长得就像动态规划的样子?
设 为前 次借用自行车的最小花费。
外层循环枚举前 次借用,内层循环枚举第 种优惠券。
转移:找到 满足:第 次借用都可以使用当前枚举的第 种优惠券
初始条件:
细节还是挺多的,,以及,,重构代码解决 的问题!
Code
//by DTTTTTTT
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 505, M = 1e5 + 5, Q = 3e5 + 5;
const ll inf = 1e18;
int n, m, r, d[N], k[N], c[N], sumq[M], totq, days[Q];
ll f[Q];
struct days {
int p, q;
}a[M];
bool cmp(struct days x, struct days y) {
return x.p < y.p;
}
int ef(int cur, int limit) {
int l = 1, r = m, mid, ret = 1;
while (l <= r) {
mid = (l + r) >> 1;
if (a[mid].p >= cur - limit + 1) ret = mid, r = mid - 1;
else l = mid + 1;
}
return ret;
}
int main() {
scanf("%d%d%d", &n, &m, &r); //n种优惠券 m天 单次借用花费r元
for (int i = 1;i <= n;++i) scanf("%d%d%d", &d[i], &k[i], &c[i]); //d:天数限制 k:次数限制 c:价格
for (int i = 1;i <= m;++i) scanf("%d%d", &a[i].p, &a[i].q); //第p天借用q次
sort(a + 1, a + m + 1, cmp);
for (int i = 1;i <= m;++i) {
sumq[i] = sumq[i - 1] + a[i].q;
for (int j = 1;j <= a[i].q;++j) {
int cur = sumq[i - 1] + j;
f[cur] = inf;
days[cur] = a[i].p;
}
}
totq = sumq[m];
d[0] = k[0] = 1, c[0] = r;
for (int i = 1;i <= totq;++i) {
for (int j = 0;j <= n;++j) {
int ind;
if (days[max(i - k[j] + 1, 1)] >= days[i] - d[j] + 1) ind = max(i - k[j], 0);
else ind = sumq[ef(days[i], d[j]) - 1];
f[i] = min(f[i], f[ind] + c[j]);
}
}
printf("%lld\n", f[totq]);
return 0;
}
I Rise of Shadows 数学/同余
Description
的时间设置如下:一天 个小时,一小时 分钟。
给定一个的时钟,问一天之内有多少整数分钟时刻满足个分针和时针夹角不超过 ()。
已知新的一天开始时,时针和分针重合。
Solution
时针的每分钟旋转的角度:
分针每分钟旋转的角度:
计入答案的整数分钟时刻 应该满足:
又
或
等价于 其中 或
线性同余方程 有解当且仅当 。
在有解时,先用欧几里得算法求出一组整数,满足 ,则是该线性方程的一个解。
由定理可知:时, 在 上有唯一解。
同样地,与互质,则在上有唯一解。
为了满足,将等式 其中 或 两边同时除以得:
由定理可知:由于与互质,在上有唯一解,在上有唯一解..........
则在上有个解。
即:对于每一个有解的 (即:满足 ),有个合法的。
而有解的的数目为:
所以答案为:
注意到当 时,,按照 或 求得的数目可能会多一个,需要特殊判断,易知这种情况的答案为。
Code
//by DTTTTTTT
#include<iostream>
#define ll long long
using namespace std;
ll H, M, A;
ll gcd(ll x, ll y) {
return y == 0 ? x : gcd(y, x % y);
}
int main() {
cin >> H >> M >> A;
if (A * 2 == H * M) {
cout << A * 2 << endl;
return 0;
}
ll d = gcd(H - 1, H * M);
cout << d * (A / d + 1 + H * M / d - (H * M - A + d - 1) / d) << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现