第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(沈阳)
D . J o u r n e y t o U n ′ G o r o D. Journey to Un'Goro D.JourneytoUn′Goro
题 意 题意 题意
构 造 长 度 为 n 的 字 符 串 , 每 个 点 是 b 或 r 。 之 后 选 两 个 点 L , R ( 1 ≤ L ≤ R ≤ n ) , 使 得 [ L , R ] 中 有 奇 数 个 r , 问 ( L , R ) 点 对 的 最 大 数 量 。 按 字 典 序 输 出 构 造 的 序 列 , 如 构造长度为n的字符串,每个点是b或r。之后选两个点L,R(1 \leq L\leq R\leq n),使得[L,R]中有奇数个r,问(L,R)点对的最大数量。按字典序输出构造的序列,如 构造长度为n的字符串,每个点是b或r。之后选两个点L,R(1≤L≤R≤n),使得[L,R]中有奇数个r,问(L,R)点对的最大数量。按字典序输出构造的序列,如 果 数 量 > 100 只 输 出 前 100 个 果数量>100只输出前100个 果数量>100只输出前100个
解 法 解法 解法
把 b 看 成 0 , r 看 成 1 , 的 到 01 数 组 b , 对 b 求 异 或 前 缀 和 。 把b看成0,r看成1,的到01数组b,对b求异或前缀和。 把b看成0,r看成1,的到01数组b,对b求异或前缀和。
( L , R ) 中 有 奇 数 个 1 ⇒ b [ r ] − b [ l − 1 ] 是 奇 数 ⇒ b [ r ] 与 b [ l − 1 ] 奇 偶 性 不 同 (L,R)中有奇数个1 \Rightarrow b[r] - b[l - 1]是奇数 \Rightarrow b[r]与b[l - 1]奇偶性不同 (L,R)中有奇数个1⇒b[r]−b[l−1]是奇数⇒b[r]与b[l−1]奇偶性不同
设 b [ 0 ∽ n ] 中 有 x 个 0 , y 个 1 , ( L , R ) 的 选 法 有 x ∗ y 种 , 当 x ∗ y = ⌊ ( n + 1 ) 2 ⌋ ⌈ ( n + 1 ) 2 ⌉ 时 最 大 。 设b[0\backsim n]中有x个0,y个1,(L,R)的选法有x*y种,当x*y= \lfloor \frac {(n+1)} {2} \rfloor \lceil \frac {(n+1)} {2} \rceil时最大。 设b[0∽n]中有x个0,y个1,(L,R)的选法有x∗y种,当x∗y=⌊2(n+1)⌋⌈2(n+1)⌉时最大。
可 以 看 出 b 数 组 中 , 0 和 1 的 个 数 都 不 能 超 过 ⌈ ( n + 1 ) 2 ⌉ , 根 据 这 个 条 件 进 行 剪 枝 。 可以看出b数组中,0和1的个数都不能超过 \lceil \frac {(n+1)} {2} \rceil,根据这个条件进行剪枝。 可以看出b数组中,0和1的个数都不能超过⌈2(n+1)⌉,根据这个条件进行剪枝。
因 为 只 要 输 出 100 组 , 可 能 能 用 d f s 过 , 事 实 证 明 也 是 可 以 的 。 因为只要输出100组,可能能用dfs过,事实证明也是可以的。 因为只要输出100组,可能能用dfs过,事实证明也是可以的。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
char s[N];
int n, m, lim, cnt;
void dfs(int u, int cnt0, int cnt1, int sum) {
if (cnt0 > lim || cnt1 > lim) return ;
if (u == n + 1) {
s[n + 1] = '\0';
cout << s + 1 << endl;
cnt ++;
if (cnt >= 100) exit(0);
}
s[u] = 'b';
int t0 = 0, t1 = 0;
if (sum & 1) t1 = 1;
else t0 = 1;
dfs(u + 1, cnt0 + t0, cnt1 + t1, sum);
s[u] = 'r';
dfs(u + 1, cnt0 + (t0 ^ 1), cnt1 + (t1 ^ 1), sum ^ 1);
}
int main() {
cin >> n;
cout << 1ll * (n + 1 >> 1) * (n + 2 >> 1) << endl;
lim = (n + 2 >> 1);
dfs(1, 1, 0, 0);
return 0;
}
F . K o b o l d s a n d C a t a c o m b s F.Kobolds and Catacombs F.KoboldsandCatacombs
题 意 题意 题意
给 一 个 数 组 a , 将 a 分 成 尽 可 能 多 的 段 , 使 得 只 对 段 内 排 序 , 能 够 使 a 变 成 升 序 给一个数组a,将a分成尽可能多的段,使得只对段内排序,能够使a变成升序 给一个数组a,将a分成尽可能多的段,使得只对段内排序,能够使a变成升序
解 法 解法 解法
设
b
为
a
稳
定
排
序
后
的
数
组
,
则
每
一
段
内
a
和
b
中
的
元
素
在
原
数
组
中
的
下
标
构
成
的
集
合
一
设b为a稳定排序后的数组,则每一段内a和b中的元素在原数组中的下标构成的集合一
设b为a稳定排序后的数组,则每一段内a和b中的元素在原数组中的下标构成的集合一
定
相
同
定相同
定相同
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000010;
struct Node {
int x, i;
bool operator < (const Node no) const {
return x < no.x;
}
}node[N];
int n, m;
int s[N], cnt;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i ++) {
scanf("%d", &node[i].x);
node[i].i = i;
}
int ans = 0;
stable_sort(node + 1, node + 1 + n);
for (int i = 1; i <= n; i ++) {
int j = i;
bool flag = 1;
do {
if (flag) cnt = 0, flag = 0;
s[j] ^= 1;
if (s[j]) cnt ++;
else cnt --;
int t = node[j].i;
s[t] ^= 1;
if (s[t]) cnt ++;
else cnt --;
j ++;
}while (cnt);
i = j - 1;
ans ++;
}
printf("%d", ans);
return 0;
}
G . T h e W i t c h w o o d G.The Witchwood G.TheWitchwood
解 法 解法 解法
降 序 排 序 后 对 前 m 个 数 求 和 降序排序后对前m个数求和 降序排序后对前m个数求和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10010;
LL a[N], n, m;
int main() {
cin >> n >>m;
for (int i = 1; i <= n; i ++) cin>> a[i];
sort(a + 1, a + 1 + n, greater<int>());
LL ans = 0;
for (int i = 1; i <= m; i ++) ans += a[i];
cout << ans;
return 0;
}
H . T h e B o o m s d a y P r o j e c t H.The Boomsday Project H.TheBoomsdayProject
题 意 题意 题意
给 定 租 一 次 车 的 价 格 r , 以 及 某 个 人 哪 一 天 要 租 车 , 那 一 天 要 租 多 少 次 车 。 有 一 些 折 扣 给定租一次车的价格r,以及某个人哪一天要租车,那一天要租多少次车。有一些折扣 给定租一次车的价格r,以及某个人哪一天要租车,那一天要租多少次车。有一些折扣 卡 , 给 定 每 张 折 扣 卡 的 有 效 天 数 , 免 费 租 车 次 数 , 售 价 。 问 这 个 人 的 最 小 花 费 。 卡,给定每张折扣卡的有效天数,免费租车次数,售价。问这个人的最小花费。 卡,给定每张折扣卡的有效天数,免费租车次数,售价。问这个人的最小花费。
解 法 解法 解法
租
车
的
总
次
数
只
有
5
e
5
,
日
期
的
编
号
有
1
e
9
,
考
虑
按
车
分
类
而
不
是
按
哪
一
天
分
类
。
租车的总次数只有5e5,日期的编号有1e9,考虑按车分类而不是按哪一天分类。
租车的总次数只有5e5,日期的编号有1e9,考虑按车分类而不是按哪一天分类。
将
单
车
拿
出
来
后
对
它
们
按
照
所
在
日
期
编
号
排
序
。
定
义
f
[
i
]
为
租
前
i
辆
车
的
最
小
花
费
。
将单车拿出来后对它们按照所在日期编号排序。定义f[i]为租前i辆车的最小花费。
将单车拿出来后对它们按照所在日期编号排序。定义f[i]为租前i辆车的最小花费。
第
i
辆
车
不
适
用
折
扣
卡
:
f
[
i
]
=
f
[
i
−
1
]
+
r
第i辆车不适用折扣卡:f[i] = f[i - 1] + r
第i辆车不适用折扣卡:f[i]=f[i−1]+r
使
用
折
扣
卡
有
如
下
转
移
方
程
如
下
,
如
果
不
优
化
会
超
时
使用折扣卡有如下转移方程如下,如果不优化会超时
使用折扣卡有如下转移方程如下,如果不优化会超时
for (int i = 1; i <= n; i ++) //枚举单车
for (int j = 1; j <= m; j ++) //枚举m张折扣卡
for (int k...) //租第k辆单车时买j号折扣卡(要保证能覆盖i)
f[i] = min(f[i], f[k-1]+c[j]); //第k辆车被折扣卡抵扣,因此从f[k-1]转移
f 数 组 是 升 序 的 , 使 用 折 扣 卡 的 转 移 就 要 尽 可 能 地 从 前 面 的 状 态 转 移 , 又 因 为 折 扣 卡 有 f数组是升序的,使用折扣卡的转移就要尽可能地从前面的状态转移,又因为折扣卡有 f数组是升序的,使用折扣卡的转移就要尽可能地从前面的状态转移,又因为折扣卡有 次 数 和 天 数 限 制 , 转 移 点 只 能 在 靠 近 i 的 某 个 范 围 内 取 。 即 在 靠 近 i 的 合 法 范 围 内 取 最 靠 次数和天数限制,转移点只能在靠近i的某个范围内取。即在靠近i的合法范围内取最靠 次数和天数限制,转移点只能在靠近i的某个范围内取。即在靠近i的合法范围内取最靠 前 的 。 对 每 种 折 扣 卡 都 用 一 个 变 量 i d 存 最 优 转 移 点 的 下 标 就 能 优 化 掉 对 k 的 枚 举 。 前的。对每种折扣卡都用一个变量id存最优转移点的下标就能优化掉对k的枚举。 前的。对每种折扣卡都用一个变量id存最优转移点的下标就能优化掉对k的枚举。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 300010;
int day[N], id[N], d[N], cnt[N], c[N], n, m, r;
LL f[N];
int main() {
cin >> m >> n >> r;
int T = n;
n = 0;
for (int i = 1; i <= m; i ++) {
cin >> d[i] >> cnt[i] >> c[i];
id[i] = 1;
}
while (T --) {
int a, b;
cin >> a >> b;
for (int i = 1; i <= b; i ++) day[++ n] = a;
}
sort(day + 1, day + 1 + n);
for (int i = 1; i <= n; i ++) {
f[i] = f[i - 1] + r;
for (int j = 1; j <= m; j ++) {
while (day[i] - day[id[j]] + 1 > d[j] || i - id[j] + 1 > cnt[j]) id[j] ++;
f[i] = min(f[i], f[id[j] - 1] + c[j]);
}
}
cout << f[n] <<endl;
return 0;
}
I . R i s e o f S h a d o w s I.Rise of Shadows I.RiseofShadows
题 意 题意 题意
一 天 有 H 小 时 , 一 小 时 有 M 分 钟 , 问 有 多 少 个 整 数 分 钟 满 足 时 针 和 分 针 的 夹 角 ≤ α 一天有H小时,一小时有M分钟,问有多少个整数分钟满足时针和分针的夹角 \leq \alpha 一天有H小时,一小时有M分钟,问有多少个整数分钟满足时针和分针的夹角≤α
α = 2 π A H M \alpha= \frac{2\pi A}{HM} α=HM2πA
解 法 解法 解法
把 表 盘 的 一 圈 分 为 H × M 格 。 把表盘的一圈分为H\times M格。 把表盘的一圈分为H×M格。
每 分 钟 , 分 针 走 2 π M 弧 度 , 也 就 是 M 格 , 时 针 走 2 π H M 弧 度 , 也 就 是 1 格 。 可 以 得 到 如 下 的 式 子 。 每分钟,分针走\frac{2\pi}{M}弧度,也就是M格,时针走\frac{2\pi}{HM}弧度,也就是1格。可以得到如下的式子。 每分钟,分针走M2π弧度,也就是M格,时针走HM2π弧度,也就是1格。可以得到如下的式子。
( 2 π M − 2 π M H ) t ≤ 2 π A M H ( m o d M H ) (\frac{2\pi}{M}-\frac{2\pi}{MH})t\leq \frac{2\pi A}{MH}(mod MH) (M2π−MH2π)t≤MH2πA(modMH)
⇒ ( H − 1 ) t ≤ A ( m o d M H ) \Rightarrow (H-1)t\leq A(modMH) ⇒(H−1)t≤A(modMH)
令 d = g c d ( H − 1 , M H ) 令d=gcd(H-1,MH) 令d=gcd(H−1,MH)
我 们 要 求 的 就 是 % M H 意 义 下 的 t 的 个 数 我们要求的就是\%MH意义下的t的个数 我们要求的就是%MH意义下的t的个数
令 0 < K ≤ A , 且 K ∣ d 令0<K\leq A,且K|d 令0<K≤A,且K∣d
对
每
个
这
样
的
K
,
在
%
M
H
的
意
义
下
,
t
的
解
的
个
数
为
g
c
d
(
H
−
1
,
M
H
)
对每个这样的K,在\%MH的意义下,t的解的个数为gcd(H-1, MH)
对每个这样的K,在%MH的意义下,t的解的个数为gcd(H−1,MH) 似乎是个定理
算 上 负 数 和 0 , 这 样 的 K 一 共 有 A d × 2 + 1 个 , 对 每 个 K , 解 的 个 数 为 d , 所 以 原 不 等 式 的 解 的 个 数 为 : 算上负数和0,这样的K一共有\frac{A}{d} \times2+1个,对每个K,解的个数为d,所以原不等式的解的个数为: 算上负数和0,这样的K一共有dA×2+1个,对每个K,解的个数为d,所以原不等式的解的个数为:
d × ( A d × 2 + 1 ) d\times(\frac{A}{d}\times2+1) d×(dA×2+1)
当 A = H M 2 时 , α = π , 正 好 是 半 圈 , 一 个 指 针 顺 时 针 与 逆 时 针 绕 半 圈 的 位 置 会 被 重 复 计 算 , 这 个 时 候 当A=\frac{HM}{2}时,\alpha=\pi,正好是半圈,一个指针顺时针与逆时针绕半圈的位置会被重复计算,这个时候 当A=2HM时,α=π,正好是半圈,一个指针顺时针与逆时针绕半圈的位置会被重复计算,这个时候 要 特 判 。 一 天 里 的 每 一 分 钟 都 是 合 法 情 况 一 共 有 H × M 种 要特判。一天里的每一分钟都是合法情况一共有H\times M种 要特判。一天里的每一分钟都是合法情况一共有H×M种
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
LL h, m, a;
cin >> h >> m >> a;
LL d = __gcd(h - 1, m * h);
if (a * 2 == h * m) cout << h * m;
else cout << d * (a / d * 2 + 1);
return 0;
}
J . D e s c e n t o f D r a g o n s J.Descent of Dragons J.DescentofDragons
看 起 来 像 主 席 树 。 但 是 不 会 。 没 找 到 题 解 , 哪 位 大 佬 有 题 解 麻 烦 给 个 链 接 顺 便 踢 我 一 下 看起来像主席树。但是不会。没找到题解,哪位大佬有题解麻烦给个链接顺便踢我一下 看起来像主席树。但是不会。没找到题解,哪位大佬有题解麻烦给个链接顺便踢我一下
K . S c h o l o m a n c e A c a d e m y K.Scholomance Academy K.ScholomanceAcademy
解 法 解法 解法
把
F
P
R
看
成
横
坐
标
x
,
T
P
R
看
成
纵
坐
标
y
。
θ
从
大
小
变
化
的
过
程
中
,
x
和
y
都
是
递
增
的
,
把FPR看成横坐标x,TPR看成纵坐标y。\theta从大小变化的过程中,x和y都是递增的,
把FPR看成横坐标x,TPR看成纵坐标y。θ从大小变化的过程中,x和y都是递增的,
即
给
定
的
函
数
是
一
个
单
调
递
增
函
数
,
且
经
过
(
0
,
0
)
和
(
1
,
1
)
两
个
点
。
函
数
构
成
了
一
些
矩
形
即给定的函数是一个单调递增函数,且经过(0,0)和(1,1)两个点。函数构成了一些矩形
即给定的函数是一个单调递增函数,且经过(0,0)和(1,1)两个点。函数构成了一些矩形
。
我
们
要
求
的
就
是
矩
形
的
面
积
和
。
θ
枚
举
关
键
的
值
就
行
,
也
就
是
题
目
中
输
入
的
数
。
从
大
。我们要求的就是矩形的面积和。\theta枚举关键的值就行,也就是题目中输入的数。从大
。我们要求的就是矩形的面积和。θ枚举关键的值就行,也就是题目中输入的数。从大
到
小
枚
举
θ
,
累
加
y
×
Δ
x
即
可
求
得
答
案
。
到小枚举\theta,累加y\times \Delta x 即可求得答案。
到小枚举θ,累加y×Δx即可求得答案。
一
个
细
节
:
两
个
数
相
同
时
,
+
的
排
在
前
面
一个细节:两个数相同时,+的排在前面
一个细节:两个数相同时,+的排在前面
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N = 1000010;
struct Node {
char c;
int x;
bool operator< (const Node &u) const {
if (x == u.x) return c < u.c;
return x < u.x;
}
}node[N];
int n, m, cnt;
double tp, fp, fn, tn;
double tpr() {
return tp / (tp + fn);
}
double fpr() {
return fp / (tn + fp);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i ++) {
char s[2];
int x;
scanf("%s%d", s, &x);
node[i] = {s[0], x};
if (s[0] == '+') fn ++;
else tn ++;
}
double pre = 0, ans = 0;
sort(node + 1, node + 1 + n);
for (int i = n; i >= 1; i --) {
if (node[i].c == '+') fn --, tp ++;
else tn --, fp ++;
ans += tpr() * (fpr() - pre);
pre = fpr();
}
printf("%.10f", ans);
return 0;
}