2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 2)
2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 2)
找个机会水了一场CF,感觉是不是选得太简单了这场……那么过于水的题目就不写出来了吧……
P.Fear Factoring
The Slivians are afraid of factoring; it’s just, well, difficult.
Really, they don’t even care about the factors themselves, just how much they sum to.
We can define \(F(n)\) as the sum of all of the factors of n; so \(F(6) = 12\) and \(F(12) = 28\). Your task is, given two integers a and b with \(a \leq b\), to calculate $$ S = \sum_{a \leq n \leq b} F(n) $$
Input
The input consists of a single line containing space-separated integers \(a\) and \(b\) \((1 \leq a \leq b \leq 10 ^ {12}, b − a \leq 10 ^ 6)\).
Output
Print \(S\) on a single line
一句话题意:定义\(F(n)\)为\(n\)的所有因数和,求\(\sum_{a \leq n \leq b} F(n)\)。
题解:
容易得到一个枚举因数的做法。我们枚举每一个因数计算贡献,那么答案式子是这样的\(S = \sum_{1 \leq d \leq b} \lfloor \frac{b}{d} \rfloor - \sum_{1 \leq d \leq a} \lfloor \frac{a}{d} \rfloor\)。拆成两次算,然后整除分块即可。注意中间直接乘会爆\(long \ \ long\),处理一下即可。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, b;
ll Solve(ll x) {
ll ans = 0;
for(ll i = 1; i <= x; ) {
ll nx = x / (x / i);
ll v = x / i;
ll c1 = (i + nx), c2 = (nx - i + 1);
if(c1 & 1) c2 /= 2;
else c1 /= 2;
ll c = c1 * c2;
ans += v * c;
i = nx + 1;
}
return ans;
}
int main() {
scanf("%lld%lld", &a, &b);
printf("%lld\n", Solve(b) - Solve(a - 1));
return 0;
}
R.Straight Shot
You have a toy robot that walks straight at a constant speed \(v\), and you wish for it to travel on the two-dimensional plane from \((0, 0)\) to \((X, 0)\). If the plane were empty, you could start the robot facing straight east from the origin, and it would walk there in \(X/v\) time. Unfortunately, between the start and the destination are n moving sidewalks, each moving directly north or south, which affect the robot’s position while it is walking.
The direction that robot is facing is not changed by the sidewalks; the robot will face in the same orientation for the entire duration of its walk. These sidewalks are aligned with the \(y\)-\(axis\) and are infinitely long. You still must get the robot to go from start to finish, but you’ll need to adjust the orientation of the robot at the start. Given that you choose this direction correctly, so that the robot arrives exactly at the destination, how long will it take the robot to get there?
One final caveat: You don’t want the toy robot to walk for too long. If the robot cannot reach the destination in at most twice the time it would take in the absence of all moving sidewalks (\(i.e., 2X/v\)), indicate this.
Input
The first line consists of three space-separated numbers \(n, X,\) and \(v (0 ≤ n ≤ 100, 1 ≤ X ≤ 1,000,000, 1.0 ≤ v ≤ 100.0).\) Note that \(v\) is not necessarily an integer.
Each of the next n lines contains three space-separated numbers \(l_i , r_i ,\) and \(v_i (0 ≤ l_1 < r_1 ≤ l_2 < r_2 ≤ \cdots ≤ l_n < r_n ≤ X; −100.0 ≤ v_i ≤ 100.0),\) describing the ith moving sidewalk. The integer \(l_i\) denotes the left edge of the sidewalk, the integer \(r_i\) denotes the right edge of the sidewalk, and the decimal number \(v_i\) denotes the speed of the sidewalk. A positive speed means the sidewalk moves north, while a negative speed means the sidewalk moves south.
Output
If the robot cannot reach the destination in at most twice the time it would take in the absence of all moving sidewalks, output \(“Too hard”\) on a single line (without quotation marks). Otherwise, output, on a single line, the travel time of the robot from the start to the destination, rounded and displayed to exactly three decimal places.
一句话题意:有一个机器人,速度为\(v\),可以确定一个初始方向,在坐标系中会有一些区间\(l_i, r_i\),使得当机器人在\([l_i, r_i]\)中前进时,会附加上一个竖直向上的大小为\(v_i\)的速度,最终需要走到\((X, 0)\)问最少需要走的时间,如果这个时间大于没有区间影响的时间的两倍,那么就输出\(Too \ \ hard\)。
题解:
怕不是个物理题,只要把速度分解成\(Vx, Vy\), 然后\(Vy\)是可以计算的,\(Vy * X = \sum_{i = 1} ^ n (r_i - l_i) * v_i\)。这个随便推推就行了。然后就可以算出\(Vx\),然后计算出时间了。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const double eps = 1e-6;
int n;
double l[N], r[N], v[N];
double X, V, sum;
int main() {
scanf("%d%lf%lf", &n, &X, &V);
double T = X / V;
for(int i = 1; i <= n; i++) scanf("%lf%lf%lf", &l[i], &r[i], &v[i]), sum += (r[i] - l[i]) * v[i];
double Vy = - sum / X;
if(V * V - Vy * Vy < eps) return puts("Too hard"), 0;
double Vx = sqrt(V * V - Vy * Vy);
double t = X / Vx;
if(T * 2.0 - t <= eps) return puts("Too hard"), 0;
printf("%.3f\n", t);
return 0;
}
T.Security Badge
You are in charge of the security for a large building, with \(n\) rooms and \(m\) doors between the rooms. The rooms and doors are conveniently numbered from \(1\) to \(n\), and from \(1\) to \(m\), respectively.
Door i opens from room \(a_i\) to room \(b_i\) , but not the other way around. Additionally, each door has
a security code that can be represented as a range of numbers \([c_i , d_i].\)
There are \(k\) employees working in the building, each carrying a security badge with a unique, integer-valued badge ID between \(1\) and \(k\). An employee is cleared to go through door \(i\) only when the badge ID \(x\) satisfies \(c_i ≤ x ≤ d_i.\)
Your boss wants a quick check of the security of the building. Given \(s\) and \(t\), how many employees can go from room \(s\) to room \(t\)?
Input
The first line of input contains three space-separated integers \(n, m,\) and \(k (2 ≤ n ≤ 1,000, 1 ≤ m ≤ 5,000, 1 ≤ k ≤ 109 ).\)
The second line of input contains two space-separated integers \(s\) and \(t (1 ≤ s, t ≤ n; s \neq t).\)
Each of the next m lines contains four space-separated integers \(a_i , b_i , c_i,\) and \(d_i (1 ≤ a_i , b_i ≤ n, 1 ≤ c_i ≤ d_i ≤ k, a_i \neq b_i)\), describing door \(i\).
For any given pair of rooms \(a\), \(b\) there will be at most one door from \(a\) to \(b\) (but there may be both a door from \(a\) to \(b\) and a door from \(b\) to \(a\))
Output
Print, on a single line, the number of employees who can reach room \(t\) starting from room \(s\)
一句话题意:有一个\(n\)个点\(m\)条边的有向图,每条边只允许\([a_i, b_i]\)之间的数通过,问\(1\)到\(k\)中有多少数能够从\(s\)走到\(t\)。
题解:
一开始没想出来怕不是智障了……首先把每一个\(a_i, b_i\)看做一个关键点,那么\(1\)到\(k\)会被关键点分成\(O(n)\)段,取每一段分别计算答案即可。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
struct edge {
int to, nxt, l, r, f;
}E[N << 2];
int head[N], s[N << 2], vis[N];
int tot, n, m, S, T, k, t, las, ans;
void Addedge(int u, int v, int l, int r) {
E[++tot].to = v; E[tot].nxt = head[u]; head[u] = tot; E[tot].l = l; E[tot].r = r; E[tot].f = 0;
}
queue<int> Q;
int Bfs() {
while(!Q.empty()) Q.pop();
Q.push(S);
memset(vis, 0, sizeof vis);
while(!Q.empty()) {
int o = Q.front(); Q.pop();
vis[o] = 1;
if(o == T) return 1;
for(int i = head[o]; ~i; i = E[i].nxt) {
int to = E[i].to;
if(E[i].f && !vis[to]) Q.push(to);
}
}
return 0;
}
void Check(int L, int R) {
for(int i = 1; i <= tot; i++) {
E[i].f = 0;
if(E[i].l <= L && E[i].r >= R) E[i].f = 1;
}
if(Bfs()) ans += R - max(las, L) + 1, las = R + 1;
}
int main() {
memset(head, -1, sizeof head);
scanf("%d%d%d%d%d", &n, &m, &k, &S, &T);
for(int i = 1, u, v, l, r; i <= m; i++) {
scanf("%d%d%d%d", &u, &v, &l, &r);
Addedge(u, v, l, r);
s[++t] = l; s[++t] = r;
}
sort(s + 1, s + 1 + t);
for(int i = 1; i < t; i++) Check(s[i], s[i + 1]);
printf("%d\n", ans);
return 0;
}
W.Grid Coloring
You have an \(m\)-by-\(n\) grid of squares that you wish to color. You may color each square either red or blue, subject to the following constraints:
• Every square must be colored.
• Colors of some squares are already decided (red or blue), and cannot be changed.
• For each blue square, all squares in the rectangle from the top left of the grid to that square must also be blue.
Given these constraints, how many distinct colorings of the grid are there? The grid cannot be
rotated.
Input
The first line of input consists of two space-separated integers \(m\) and \(n (1 ≤ m, n ≤ 30).\) Each of the next \(m\) lines contains \(n\) characters, representing the grid. Character ‘B’ indicates squares that are already colored blue. Similarly, ‘R’ indicates red squares. Character ‘.’ indicates squares that are not colored yet.
Output
Print, on a single line, the number of distinct colorings possible.
一句话题意:有一个\(n * m\)的网格图,每个格子初始可能为蓝色或者红色或者没有上色,你要个每个没上色的点定一个颜色,满足任意一个蓝色点的左上方一定都是蓝色,求方案数。
题解:
感觉已经像是老套路了。先把一些能够确定颜色的点涂上颜色,那么此时图会变成左上角一些蓝色,右下角一些红色的图,然后中间是无色的。考虑最终的答案图,红色格子的个数一定是递增的,那么我们开始\(Dp\),记\(f[i][j]\)表示考虑了前\(i\)列,红色格子已经涂到了第\(j\)行的方案数,预处理出第\(i\)列在结束时红色格子可能的个数,转移就非常好转移了。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 35;
int n, m;
int Mp[N][N], dn[N], up[N];
ll f[N][N];
char s[N];
void ColorB(int x, int y) {
for(int i = 1; i <= x; i++) {
for(int j = 1; j <= y; j++) {
if(!Mp[i][j] || Mp[i][j] == -1) Mp[i][j] = -1;
else {
puts("0");
exit(0);
}
}
}
return ;
}
void ColorR(int x, int y) {
for(int i = x; i <= n; i++) {
for(int j = y; j <= m; j++) {
if(!Mp[i][j] || Mp[i][j] == 1) Mp[i][j] = 1;
else {
puts("0");
exit(0);
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%s", s + 1);
for(int j = 1; j <= m; j++) {
if(s[j] == 'R') Mp[i][j] = 1;
else if(s[j] == 'B') Mp[i][j] = -1;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(Mp[i][j] == -1) ColorB(i, j);
else if(Mp[i][j] == 1) ColorR(i, j);
}
}
for(int j = 1; j <= m; j++) {
int fs = n + 1;
for(int i = 1; i <= n; i++) {
if(!Mp[i][j]) {
dn[j] = i;
if(!up[j]) up[j] = i;
}
if(Mp[i][j] == 1) fs = min(fs, i);
}
if(!up[j]) up[j] = dn[j] = fs;
else dn[j]++;
}
f[0][n + 1] = 1;
for(int i = 1; i <= m; i++) {
for(int j = up[i]; j <= dn[i]; j++) {
for(int k = n + 1; k >= j; k--) {
f[i][j] += f[i - 1][k];
}
}
}
ll ans = 0;
for(int i = up[m]; i <= dn[m]; i++) ans += f[m][i];
printf("%lld\n", ans);
return 0;
}
然后……似乎就没了吧……还有一个\(V\)似乎乱搞搞就行了,感觉细节有点多就没写,假装自己以后会补吧……
真的是感觉自己越来越菜了……