A: Broadcast Stations
题目大意
给定一棵树,选一些节点 ,赋予 表示节点能覆盖到距离不超过 的所有点,如果 ,不能覆盖本身,问 。
题解
树形DP。
B: Connect3
题目大意
有一个4*4的竖着放的板,每次可以选择一列从上方放一个黑色或白色的圆盘,圆盘会从下往上磊。已知黑方先选择第x列放了黑色圆盘,问白方最后选择第b列圆盘掉在了第a行恰好白方胜利的情况数有多少。胜利指的是横着、斜着、竖着连续3个圆盘颜色相同即为胜利。
(图片从维基找的)
题解
因为范围只有4*4,又有情况的限制,因此合法的情况不会很多,因此我们全部爆搜出来就好了。。
#include <cstdio>
#include <set>
#include <utility>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
set<pair<int, int>> vis;
int a, b, ans = 0;
int board[5][5];
int loc(int x, int y)
{
return (x - 1) * 4 + (y - 1);
}
bool win(int p)
{
int i, j;
FOR(i,1,4) FOR(j,1,4)
{
if (i <= 2 && board[i][j] == p && board[i + 1][j] == p && board[i + 2][j] == p)
return true;
if (j <= 2 && board[i][j] == p && board[i][j + 1] == p && board[i][j + 2] == p)
return true;
if (i <= 2 && j <= 2 && board[i][j] == p && board[i + 1][j + 1] == p && board[i + 2][j + 2] == p)
return true;
if (i <= 2 && j >= 3 && board[i][j] == p && board[i + 1][j - 1] == p && board[i + 2][j - 2] == p)
return true;
}
return false;
}
void dfs(int black, int white, int turn, int x, int y)
{
int i, j;
if (vis.count(make_pair(black, white)))
return;
vis.insert(make_pair(black, white));
if (win(2) || win(3))
{
if (win(3) && turn == 2 && x == a && y == b)
++ans;
return;
}
if (board[a][b])
return;
FOR(i,1,4) FOR(j,1,4) if (!board[j][i])
{
board[j][i] = turn;
dfs(black | ((turn == 2) << loc(j, i)), white | ((turn == 3) << loc(j, i)), turn ^ 1, j, i);
board[j][i] = 0;
break;
}
}
int main()
{
int x;
scanf("%d%d%d", &x, &a, &b);
board[1][x] = 2;
dfs(1 << loc(1, x), 0, 3, 1, x);
printf("%d", ans);
return 0;
}
C: Game Map
题目大意
给定一个无向图,没有重边,连通。一个合法的序列要求相邻两个元素间有边相连,而且后一个元素比前一个元素的度数要更大,求最长的合法序列的长度。
题解
我们在按度数排序了以后,边就只能向后指(因为向前指的边表示从度数大的点走到度数小的点,是不合法的,可删去),而且端点度数相同的边也要删去(因为要度数严格大于)。那么图就变成了一个DAG,问题变成在DAG上求最长路,DP即可。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100005;
int id[N], dp[N];
vector<int> g1[N], g2[N];
bool cmp(int x, int y) {
return g1[x].size() < g1[y].size();
}
int main() {
int n, m, i, x, y;
scanf("%d%d", &n, &m);
for (i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
g1[x].push_back(y);
g1[y].push_back(x);
}
for (i = 0; i < n; i++) id[i] = i;
sort(id, id + n, cmp);
for (i = 0; i < n; i++)
for (auto j : g1[i])
if (g1[i].size() < g1[j].size()) g2[i].push_back(j);
for (i = 0; i < n; i++)
for (auto y : G[id[i]])
dp[y] = max(dp[y], dp[id[i]] + 1);
int ans = 0;
for (i = 0; i < n; i++) ans = max(ans, dp[i]);
printf("%d", ans + 1);
return 0;
}
D: Happy Number
题目大意
给定函数 表示 每个十进制位的平方和。对 不断应用函数 ,最后可能回到 或者到 ,问某个数 经过多次 后是否能到 。
题解
暴力。。
#include <cstdio>
#include <cstring>
int f(int x) {
int s = 0;
while (x > 0) {
int y = x % 10;
s += y * y;
x /= 10;
}
return s;
}
int a[1000];
bool test(int x) {
if (a[x] != -1) return a[x];
return a[x] = test(f(x));
}
int main() {
memset(a, -1, sizeof a);
a[1] = 1;
a[4] = 0;
for (int i = 2; i < 1000; i++) test(i);
int n;
scanf("%d", &n);
n = f(n);
if (a[n]) puts("HAPPY");
else puts("UNHAPPY");
return 0;
}
E: How Many to Be Happy?
题目大意
有一个无向图( ),边有边权, 表示对于某条边 ,至少删去多少条边才能使 可能是MST的边。求
题解
对于每条边,边权小于它的边建立容量为1的边,e的两端点分别为源点和汇点,跑一次最大流就能求出必割边有几条。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,j,k) for(i=j;i<k;++i)
using namespace std;
const int N = 105, M = 10050, inf = 2147483647;
struct Edge {
int u, v, w;
bool operator< (const Edge &b) const {
return w < b.w;
}
} e[M];
int h[N], p[M], v[M], w[M], edge = 1, s, t, n;
int level[N], vis[N], q[N];
void add(int a, int b, int c) {
p[++edge] = h[a]; v[edge] = b; w[edge] = c; h[a] = edge;
p[++edge] = h[b]; v[edge] = a; w[edge] = 0; h[b] = edge;
}
bool bfs() {
int i, x, f = 0, r = 0;
memset(level, -1, sizeof level);
q[r++] = s; level[s] = 0;
while (f < r) {
x = q[f++];
for (i = h[x]; i; i = p[i])
if (w[i] && level[v[i]] == -1) {
level[v[i]] = level[x] + 1;
q[r++] = v[i];
}
}
return level[t] != -1;
}
int dfs(int x, int low) {
int i, tmp, res = 0;
if (x == t) return low;
for (i = h[x]; i && res < low; i = p[i])
if (w[i] && level[v[i]] == level[x] + 1) {
tmp = dfs(v[i], min(low - res, w[i]));
w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
}
if (!res) level[x] = -1;
return res;
}
int dinic() {
int ans = 0, i;
while (bfs()) {
ans += dfs(s, inf);
}
return ans;
}
int main() {
int m, i, j, ans = 0;
scanf("%d%d", &n, &m);
rep(i,0,m) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
sort(e, e + m);
rep(i,0,m) {
memset(h, 0, sizeof h); edge = 1;
s = e[i].u, t = e[i].v;
rep(j,0,i) if (e[j].w < e[i].w)
add(e[j].u, e[j].v, 1), add(e[j].v, e[j].u, 1);
ans += dinic();
}
printf("%d\n", ans);
return 0;
}
F: Philosopher’s Work
题目大意
题解
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
#define x first
#define y second
P tr1(P p) {
return P(p.y,p.x);
}
P tr2(P p) {
return P(-p.y, -p.x);
}
P add(P p0, P p1) {
return P(p0.x + p1.x, p0.y + p1.y);
}
P a[4] = {P(0,0), P(0,1), P(1,1), P(1,0)};
P solve(ll n, ll m) {
if (n == 2) {
return a[m];
}
ll t = n * n / 4;
ll pos = m / t;
if (pos == 0) {
return tr1(solve(n / 2, m % t));
}
else if (pos == 1) {
return add(solve(n / 2, m % t), P(0, n / 2));
}
else if (pos == 2) {
return add(solve(n / 2, m % t), P(n / 2, n / 2));
}
else {
return add(tr2(solve(n / 2, m % t)), P(n - 1, n / 2 - 1));
}
}
int main() {
ll n, m;
scanf("%lld%lld", &n, &m);
--m;
P ans = add(P(1, 1), solve(n, m));
cout << ans.x << " " << ans.y << endl;
}
G: Rectilinear Regions
题目大意
有一个阶梯型折线L和U,L和U可能相交后会围成一些区域,求上边界为L,下边界为U的区域个数和总面积。
题解
H: Rock Paper Scissors
题目大意
机器有一个是由石头剪刀布组成的序列,你有一个较短的石头剪刀布序列,你可以选择机器序列的一个等长的子序列玩游戏,对应位如果你赢了算1分,问你最多能赢多少分。
题解
队友想出来的FFT。。
I: Slot Machines
题目大意
你有一个数列,后 位可能有循环节,而且允许最末尾的循环节不完整(尾部被截断),比如123 123 12是合法的,循环节p长度为3,问 最大时的 和 为多少
题解
求循环节嘛。。KMP!反转数列后枚举 即可。 就表示前 位的循环节长度。原理参考 https://blog.csdn.net/huanghongxun/article/details/53213871
#include <cstdio>
#include <cstring>
const int N = 1000005;
int next[N], p[N];
int main() {
int t, i, j, n, ansk, ansp;
scanf("%d", &n); ansk = ansp = 2 * n;
for (i = n; i >= 1; --i) scanf("%d", &p[i]);
for (i = 0, j = 2; j <= n; ++j) {
while (i && p[j] != p[i + 1]) i = next[i];
if (p[j] == p[i + 1]) ++i;
next[j] = i;
}
for (i = 1; i <= n; ++i) {
int p = i - next[i], k = n - i;
if (p + k < ansp + ansk || p + k == ansp + ansk && p < ansp)
ansp = p, ansk = k;
}
printf("%d %d\n", ansk, ansp);
return 0;
}
K: Untangling Chain
题目大意
给定一个机器人的运动轨迹,机器人最开始向右,每次只能转90度并至少走1格,轨迹可能有相交处,修改轨迹线段的长度使得轨迹不相交
题解
因为每条线段长度都随便改。。所以我们每次走都走到走过最远处再远一格,记录top、bottom、left、right分别表示走过最上方的y、最下方的y、最左的x和最右的x,向左走走到最左的x-1,其他类似。
#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
int left = 0, top = 0, bottom = 0, right = 0;
int x = 0, y = 0, n, dir = 0, d;
scanf("%d", &n);
while (n--)
{
scanf("%*d %d", &d);
switch (dir)
{
case 0:
printf("%d ", (right + 1) - x);
x = right + 1;
break;
case 1:
printf("%d ", (top + 1) - y);
y = top + 1;
break;
case 2:
printf("%d ", x - (left - 1));
x = left - 1;
break;
case 3:
printf("%d ", y - (bottom - 1));
y = bottom - 1;
break;
}
right = max(right, x); left = min(left, x);
top = max(top, y); bottom = min(bottom, y);
dir = (dir + d + 4) % 4;
}
return 0;
}
L: Vacation Plans
题目大意
有3个图,从1出发,同时到达每张图的相应终点。走过一条边的时间为1,同时还可以选择在某个点停留一段整数时间。经过一条边和停留在点上都有代价g和h,问总代价最小是多少。
题解
队友有种神奇水法,卡时过了。。留坑。