A: Advertising Strategy
题目大意
题解
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {
ll n, k;
scanf("%lld%lld", &n, &k);
ll ans = 154164214511LL;
if (n <= 2) ans = 1;
for (ll t = 1; t < k; ++t) {
ll day = 1, x = t, r = k - t;
while (x + r < n) {
x += min(x, (n - x) / 2);
++day;
}
ans = min(ans, day);
}
printf("%lld", ans);
return 0;
}
C: Carpet
题目大意
现在有 的网格,求一种方案使得至多 个节点的树放在上面每个节点占一格而且边不相交。
题解
注意到网格长度要大于节点数,那么我们甚至可以每列只放一个节点(虽然实际是不需要的)。然后宽度比较小,我们要处理这个问题。如果我们进行了树链剖分,那么根到任意节点的轻链数是不超过
的,而
,因此是可以放下的。横着铺重链竖着铺轻链即可。这样每过一次轻链y坐标就+1。
比如如果1有几个子节点,其中一个是重儿子,与1在同一行,剩下的都会在第2行,而其他的在2行的儿子如果有重儿子,那么也都在2行,所以我们可以这么做:将重链堆在同一行,也就是直接每行往右边垒即可。
注意,选择最长路而不是重链是不对的。很容易构造出这样的数据:
1-2-3-4-5-6-7-8
\
9-10-11-12-13-14
\
15-16-17-18
\
19-20
如果点数增加到500层数就会超过20层。训练的时候2支队伍这么做水过去了。。
#include<cstdio>
const int N = 100005, M = N * 2;
int h[N], p[M], v[M], edge, son[N], sz[N], ansx[N], ansy[N], tot[21];
void add(int a, int b) {
p[++edge] = h[a]; v[edge] = b; h[a] = edge;
}
void dfs(int t, int fa) {
sz[t] = 1;
for (int i = h[t]; i; i = p[i])
if (v[i] != fa) {
dfs(v[i], t);
sz[t] += sz[v[i]];
if (sz[v[i]] > sz[son[t]])
son[t] = v[i];
}
}
void dfs2(int t, int fa, int y) {
ansx[t] = ++tot[y];
ansy[t] = y;
for (int i = h[t]; i; i = p[i])
if (v[i] != fa && v[i] != son[t])
dfs2(v[i], t, y + 1);
if (son[t]) dfs2(son[t], t, y);
}
int main() {
int n, i, x, y;
scanf("%d", &n);
for (i = 1; i < n; ++i) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1, 0);
dfs2(1, 0, 1);
for (i = 1; i <= n; ++i)
printf("%d %d\n", ansx[i], ansy[i]);
return 0;
}
D: Decoding of Varints
题解
大水题。
#include <cstdio>
const int N = 10005;
typedef unsigned long long ull;
int a[N];
int main() {
int n, i;
scanf("%d", &n);
for (i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (i = 1; i <= n; ++i) {
ull num = 0, base = 1;
while (i <= n && a[i] >= 128) {
num += (a[i] - 128) * base;
base *= 128;
++i;
}
num += a[i] * base;
if (num & 1)
printf("%lld\n", -((num - 1) / 2) - 1);
else
printf("%lld\n", num / 2);
}
return 0;
}
F: Fake or Leak?
题目大意
首先是ACM赛制,告诉你封榜前的排名,问比赛结束后的排名是否合法。
题解
因为给定的是部分连续的终榜。对于不在终榜里的队伍,只有一题都没过和AK 2种情况需要考虑。如果一题都没过,那么只可能比部分终榜中最后的队伍差,如果AK了,那么只可能比部分终榜中最强的队伍强。否则将破坏连续性。
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const int N = 1005, P = 27;
int n;
struct Team {
int solved, penalty, lastSubmission;
string name;
char state[P];
int submission[P], time[P];
Team() {
solved = penalty = lastSubmission = 0;
}
bool operator< (const Team &b) const {
if (solved != b.solved) return solved > b.solved;
if (penalty != b.penalty) return penalty < b.penalty;
if (lastSubmission != b.lastSubmission) return lastSubmission < b.lastSubmission;
return name < b.name;
}
void submit(int i, char type, int sub, int t) {
state[i] = type;
submission[i] = sub;
time[i] = t;
if (state[i] == '+') {
++solved;
penalty += t + (sub - 1) * 20;
lastSubmission = max(lastSubmission, t);
}
}
void killAll() {
for (int i = 0; i < n; ++i) {
if (state[i] == '+') continue;
if (state[i] == '.')
submit(i, '+', 1, 240);
else
submit(i, '+', submission[i] + 1, 240);
}
}
} a[N];
map<string, int> mp;
int final[N], infinal[N];
int main() {
int sub, t, m, k, i, j, f;
char s[20];
scanf("%d%d%d", &n, &m, &k);
for (i = 0; i < m; i++) {
scanf("%s", s);
a[i].name = s;
mp[a[i].name] = i;
for (j = 0; j < n; j++) {
scanf("%s%d%d", s, &sub, &t);
a[i].submit(j, s[0], sub, t);
}
}
for (f = 0; f < k; f++) {
scanf("%s", s);
i = mp[s];
final[f] = i;
infinal[i] = 1;
a[i] = Team();
for (j = 0; j < n; j++) {
scanf("%s%d%d", s, &sub, &t);
a[i].submit(j, s[0], sub, t);
}
}
for (int i = 0; i < k - 1; i++) {
if (!(a[final[i]] < a[final[i + 1]])) {
puts("Fake");
return 0;
}
}
Team& top = a[final[0]];
Team& bottom = a[final[k - 1]];
for (int i = 0; i < m; i++) {
if (infinal[i]) continue;
if (bottom < a[i]) continue;
if (a[i] < top) continue;
a[i].killAll();
if (a[i] < top) continue;
puts("Fake");
return 0;
}
puts("Leaked");
return 0;
}
G: God of Winds
题目大意
一个格子的风会导致4条边的风向和风力发生变化,给定网格中各条边的风向和风力,求哪些格子里有风,风向是顺时针还是逆时针,风力多大。
题解
如果我们令
表示第i行第j列的格子的风力,那么存在式子:
那么一个等式转化成两个不等式,就是差分约束系统了。
#include <cstdio>
typedef long long ll;
const int K = 512, N = K * K, M = N * 8;
int h[N], p[M], v[M], w[M], edge = 0;
int vis[N], id[K][K], cnt = 0;
int r[K][K], c[K][K];
ll f[N];
void add(int a, int b, int c) {
p[++edge] = h[a]; v[edge] = b; w[edge] = c; h[a] = edge;
}
bool dfs(int x, ll now) {
if (vis[x]) {
return f[x] == now;
}
vis[x] = 1;
f[x] = now;
for (int i = h[x]; i; i = p[i])
if (!dfs(v[i], now + w[i]))
return false;
return true;
}
int main() {
int n, m, i, j;
scanf("%d%d", &n, &m);
for (i = 0; i < n; ++i)
for (j = 0; j < m; ++j) {
scanf("%d%d", &r[i][j], &c[i][j]);
id[i][j] = cnt++;
}
for (i = 0; i < n; ++i)
for (j = 0; j < m; ++j) {
int self = id[i][j];
int other = id[(i - 1 + n) % n][j];
add(self, other, r[i][j]);
add(other, self, -r[i][j]);
other = id[i][(j - 1 + m) % m];
add(other, self, c[i][j]);
add(self, other, -c[i][j]);
}
for (i = 0; i < cnt; ++i)
if (!vis[i])
if (!dfs(i, 0))
return puts("No"), 0;
return puts("Yes"), 0;
}
H: Hilarious Cooking
题目大意
对于一个数列( ),已知部分元素(下标和元素本身),问是否存在一种填数列的方案使得数列各个元素的和=T。其中数列相邻两个元素的差的绝对值不能超过1。
题解
首先很容易想到求出数列的和的最大值和最小值再判断T是否在范围内(因为我们不需要具体的数列方案,而且满足题目要求的前提下我们连续(在非负整数集内)地调整数列元素和)。
既然差的绝对值不能超过1,那么两个点之间的元素只有可能是先45度上升(可能会有一格水平)再45度下降(最大值);或者相反(最小值)。那么这题就很简单了。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100005;
ll a[N], b[N];
ll count(ll low, ll high) {
if (low > high) return 0;
if (high < 0) return 0;
if (low < 0) low = 0;
return (low + high) * (high - low + 1) / 2;
}
ll compute_low(ll w, ll y1, ll y2) {
ll h = y2 - y1;
ll diff = (w + h) / 2;
ll low = y2 - diff;
if ((w + h) & 1) {
return count(low, y1) + count(low, y2);
} else {
return count(low, y1) + count(low, y2) - max(low, 0ll);
}
}
ll compute_high(ll w, ll y1, ll y2) {
ll h = y2 - y1;
ll diff = (w + h) / 2;
ll high = y1 + diff;
if ((w + h) & 1) {
return count(y1, high) + count(y2, high);
} else {
return count(y1, high) + count(y2, high) - high;
}
}
bool check(ll t, ll n, ll m, ll sum) {
ll low = count(max(0ll, b[1] - (a[1] - 1)), b[1])
+ count(max(0ll, b[m] - (n - a[m])), b[m]) - sum;
ll high = count(b[1], b[1] + (a[1] - 1))
+ count(b[m], b[m] + (n - a[m])) - sum;
for (int i = 2; i <= m; ++i) {
ll deltaY = abs(b[i] - b[i - 1]);
ll deltaX = abs(a[i] - a[i - 1]);
if (deltaY > deltaX)
return false;
ll mn = min(b[i - 1], b[i]);
ll mx = max(b[i - 1], b[i]);
low += compute_low(deltaX, mn, mx);
high += compute_high(deltaX, mn, mx);
}
return low <= t && t <= high;
}
int main() {
int n, m, i;
ll t, sum = 0;
scanf("%lld%d%d", &t, &n, &m);
for (i = 1; i <= m; ++i) {
scanf("%lld%lld", &a[i], &b[i]);
sum += b[i];
}
puts(check(t, n, m, sum) ? "Yes" : "No");
return 0;
}