A: Smooth Sequences
如果一个序列是光滑的仅当相邻元素差的绝对值不超过d。称一个序列是半光滑的仅当修改至多一个元素使得序列变得光滑。给定序列询问是否光滑。
题解
半光滑的有3种情况,一种是连续两端差均不满足d,这种判断两端点差不超过2d即可,一种是一端的差不满足d,随便改,一种是中间某段不满足d,判断修改左右端点即可。
#include <bits/stdc++.h>
using namespace std;
int a[1005], n;
bool check_smooth()
{
for (int i = 2; i <= n; ++i)
if (abs(a[i] - a[i - 1]) > d)
return false;
return true;
}
bool check_semi_smooth()
{
vector<int> seg;
for (int i = 2; i <= n; ++i)
if (abs(a[i] - a[i - 1]) > d)
seg.push_back(i);
if (seg.size() > 2) return false;
if (seg.size() == 2)
{
if (seg[0] + 1 != seg[1]) return false; // 连续两段
return abs(a[seg[0] - 1] - a[seg[1]]) <= d * 2;
}
else
{
if (seg[0] == 2 || seg[0] == n) return true; // 断点随便改
return abs(a[seg[0] - 2] - a[seg[0]]) <= d * 2 || abs(a[seg[0] - 1] - a[seg[0] + 1]) <= d * 2;
}
}
int main()
{
int i, d;
while (scanf("%d", &n) == 1 && n)
{
scanf("%d", &d);
for (i = 1; i <= n; ++i) scanf("%d", &a[i]);
if (check_smooth() || check_semismooth())
puts("Y");
else
puts("N");
}
return 0;
}
B: Threesome
给定无向图,取3个点,两两直接相连,且相连边和最大。
题解
暴力
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
int c[305][305];
int main()
{
int T, i, j, k, n, m, a, b, d, ans;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
m = n * (n - 1) / 2;
ans = 0;
FOR(i,1,m)
{
scanf("%d%d%d", &a, &b, &d);
c[a][b] = c[b][a] = d;
}
FOR(i,1,n) FOR(j,i+1,n) FOR(k,j+1,n)
ans = max(ans, c[i][j] + c[i][k] + c[j][k]);
printf("%d\n", ans);
}
return 0;
}
C: Coefficient Computation
计算 C(i,j) C ( i , j ) 在 d d 进制下的表示。
题解
Java大法好。
虽然C写并不会更长,但Java无脑呀233。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
public static void main(String[] args) {
BigInteger[][] C = new BigInteger[301][];
for (int i = 0; i <= 300; ++i)
C[i] = new BigInteger[301];
for (int i = 0; i <= 300; ++i)
for (int j = 0; j <= 300; ++j)
C[i][j] = BigInteger.valueOf(0);
for (int i = 0; i <= 300; ++i) {
C[i][0] = C[i][i] = BigInteger.valueOf(1);
for (int j = 1; j < i; ++j)
C[i][j] = C[i - 1][j - 1].add(C[i - 1][j]);
}
Scanner scan = new Scanner(System.in);
int T = scan.nextInt();
for (int i = 0; i < T; ++i) {
int n = scan.nextInt(), k = scan.nextInt(), d = scan.nextInt();
System.out.println(C[n][k].toString(d));
}
}
}
D: Network Report
输出长度为x的最短路有多少条。
题解
Floyd暴力即可
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 300;
const int inf = 0x3f3f3f3f;
int dis[N][N];
int sum[N];
int main()
{
int i, j, n, m, a, b;
while (scanf("%d", &n) == 1 && n)
{
scanf("%d", &m);
FOR(i,1,n) FOR(j,1,n) dis[i][j] = inf;
FOR(i,1,n) dis[i][i] = 0;
FOR(i,1,m)
{
scanf("%d%d", &a, &b);
++a; ++b;
dis[a][b] = dis[b][a] = 1;
}
FOR(k,1,n) FOR(i,1,n) FOR(j,1,n)
if (dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
FOR(i,1,n) sum[i] = 0;
FOR(i,1,n) FOR(j,1,n)
if (i != j && dis[i][j] < inf) ++sum[dis[i][j]];
FOR(i,1,n) if (sum[i])
printf("%d %d\n", i, sum[i]);
}
return 0;
}
F: Magic Sequence Pairs
可以用网络流做,但其实没有必要的。
G: Family Gathering at Christmas
线段树
I: Tracer Deployment
二分图匹配
K: Assigning Frequencies
不超过85个数据,给出不超过25个点的图,问是否存在一个方案使得将所有点染成3种颜色且相邻点颜色不同。
题解
显然是爆搜。
#include <bits/stdc++.h>
using namespace std;
int c[100], n;
bool G[30][30];
bool dfs(int v) {
for (int u = 0; u < v; ++u)
if (G[u][v] && c[u] == c[v])
return false;
if (v == n - 1) return true;
for (int i = 0; i < 3; ++i) {
c[v + 1] = i;
if (dfs(v + 1)) return true;
}
return false;
}
int main() {
int T, a, b, m, i;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
memset(G, 0, sizeof G);
for (i = 0; i < m; ++i) {
scanf("%d%d", &a, &b);
G[a][b] = G[b][a] = true;
}
if (dfs(0)) puts("Y");
else puts("N");
}
return 0;
}
L: Finding the Bases
给定一个字符串,通过切割成多个字符串提取循环节的方式压缩它,并使得提取出的循环节总长度最小。
题解
看到数据范围。。。居然可以过,没意思没意思。
众所周知,KMP可以求循环节,也即是
i−next[i]
i
−
n
e
x
t
[
i
]
,所以求n次next数组。
我们令
ri,j
r
i
,
j
表示区间
[i,j]
[
i
,
j
]
的循环节长度最小是多少。
fj
f
j
表示对于
[1..j]
[
1..
j
]
,循环节最小长度是多少。
那么有
fj=min{fi−1+ri,j}
f
j
=
min
{
f
i
−
1
+
r
i
,
j
}
,
ri,j=(j−i+1)−nxtj−i+1
r
i
,
j
=
(
j
−
i
+
1
)
−
n
x
t
j
−
i
+
1
。
#include <bits/stdc++.h>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
const int N = 10005, inf = 0x3f3f3f3f;
int nxt[N], r[N][N], f[N];
char s[N];
void get_next(const char *s) {
int len = strlen(s);
nxt[0] = -1;
for (int i = 1, j; i < len; ++i) {
j = nxt[i - 1];
while (j != -1 && s[j + 1] != s[i]) j = nxt[j];
if (s[j + 1] == s[i]) nxt[i] = j + 1;
else nxt[i] = -1;
}
}
int main() {
int T, i, j, n;
scanf("%d", &T);
while(T--)
{
scanf("%s", s + 1);
n = strlen(s + 1);
f[0] = 0;
FOR(i,1,n) f[i] = inf;
FOR(i,1,n)
{
get_next(s + i);
FOR(j,1,n)
{
int len = (j - i) - nxt[j - i];
if ((nxt[j - i] + 1) % len == 0) r[i][j] = len;
else r[i][j] = j - i + 1;
f[j] = min(f[j], f[i - 1] + r[i][j]);
}
}
printf("%d\n", f[n]);
}
return 0;
}
M: Annual Congress of MUD
嘉年华一共有D天,每个人在 [xi,yi] [ x i , y i ] 这几天来玩,每天都有一次盛会,但一个人必须且仅能参加一次盛会,我们希望盛会人数最多的时候最少,这样可以平衡盛会出席的人数。现在给定n个人按顺序申请来玩的时间,如果某个人申请来玩后盛会人数最多的时候是否会增加,如果是输出这个人的编号。
题解
对于前i个人我们都要知道局部的最优解。判断是否比i-1个人的情况多1(差值不可能比1还大)。那么我们跑最大流即可。源点连向人,容量为1,人连向能去的那些天,容量为1,每天连向汇点,容量为盛会人数最多可能多少人。这个多少人如果我们只要求全局的最优,那么二分即可。否则如果不满流,连向汇点的边容量+1就一定能满流了。注意这样连边显然会TLE,因为边数有 N∗D+N+D N ∗ D + N + D 条,点数也有 N+D+2 N + D + 2 个。我们修改构图,鉴于整个图容量为1的边很多,我们直接构造D*D个点表示区间,区间连向包含的天。那么我们每次加人,就相当于源点连向某个区间的边的容量+1,这样图就只有 D×D+D+2 D × D + D + 2 个点了。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int inf = 0x3f3f3f3f, N = 11000, M = 450000;
int level[N], cnt = 1, cur[N], v[M], p[M], h[N], q[M], s = 0, t, w[M];
int filled[N], x[N], y[N], id[25][25];
void add(int a, int b, int c) {
p[++cnt] = h[a]; v[cnt] = b; w[cnt] = c; h[a] = cnt;
p[++cnt] = h[b]; v[cnt] = a; w[cnt] = 0; h[b] = cnt;
}
bool bfs() {
int f = 0, r = 0, u, i;
for (i = s; i <= t; ++i) level[i] = -1;
q[r++] = s; level[s] = 1;
while (f < r) {
u = q[f++];
for (i = h[u]; i; i = p[i]) {
if (w[i] && level[v[i]] == -1) {
level[v[i]] = level[u] + 1;
q[r++] = v[i];
}
}
}
return level[t] > 0;
}
int dfs(int u, int low) {
int i, tmp = 0, res = 0;
if (u == t) return low;
for (i = cur[u]; i && res < low; i = p[i]) {
if (w[i] && level[v[i]] == level[u] + 1) {
tmp = dfs(v[i], min(w[i], low - res));
w[i] -= tmp; w[i ^ 1] += tmp; res += tmp;
if (w[i]) cur[u] = i;
}
}
if (!res) level[u] = -1;
return res;
}
int dinic() {
int ans = 0, i;
while (bfs()) {
for (i = s; i <= t; ++i) cur[i] = h[i];
ans += dfs(s, inf);
}
return ans;
}
int main() {
int i, j, k, l, r, mid, n, d, ans;
while (scanf("%d", &n) == 1 && n)
{
scanf("%d", &d);
vector<int> e, res;
s = 0; t = n;
cnt = 1;
memset(h, 0, sizeof h);
for (i = 1; i <= n; ++i)
{
filled[i] = 0;
scanf("%d%d", &x[i], &y[i]);
add(s, i, 1);
}
for (i = 1; i <= d; ++i)
for (j = i; j <= d; ++j)
id[i][j] = ++t;
for (i = 1; i <= d; ++i)
for (j = i; j <= d; ++j)
for (k = i; k <= j; ++k)
add(id[i][j], t + k, 1);
for (i = 1; i <= d; ++i)
{
add(t + i, t + d + 1, 0);
e.push_back(cnt - 1);
}
t = t + d + 1;
int mx = 0, flow = 0;
for (i = 1; i <= n; ++i)
{
add(i, id[x[i]][y[i]], inf);
do
{
flow += dinic();
if (flow == i)
break;
else
for (auto j : e)
w[j]++;
} while (1);
int x = -1;
for (j = h[i]; j; j = p[j])
if (v[j] > n && w[j ^ 1])
{
x = v[j] - n;
break;
}
if (x == -1) continue;
filled[x]++;
if (filled[x] > mx)
{
res.push_back(i);
mx = filled[x];
}
}
for (i = 0; i < res.size(); ++i)
printf("%d%c", res[i], i + 1 == res.size() ? '\n' : ' ');
}
return 0;
}