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
計算 在 進制下的表示。
題解
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可以求循環節,也即是 ,所以求n次next數組。
我們令 表示區間 的循環節長度最小是多少。 表示對於 ,循環節最小長度是多少。
那麼有 , 。
#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天,每個人在 這幾天來玩,每天都有一次盛會,但一個人必須且僅能參加一次盛會,我們希望盛會人數最多的時候最少,這樣可以平衡盛會出席的人數。現在給定n個人按順序申請來玩的時間,如果某個人申請來玩後盛會人數最多的時候是否會增加,如果是輸出這個人的編號。
題解
對於前i個人我們都要知道局部的最優解。判斷是否比i-1個人的情況多1(差值不可能比1還大)。那麼我們跑最大流即可。源點連向人,容量爲1,人連向能去的那些天,容量爲1,每天連向匯點,容量爲盛會人數最多可能多少人。這個多少人如果我們只要求全局的最優,那麼二分即可。否則如果不滿流,連向匯點的邊容量+1就一定能滿流了。注意這樣連邊顯然會TLE,因爲邊數有 條,點數也有 個。我們修改構圖,鑑於整個圖容量爲1的邊很多,我們直接構造D*D個點表示區間,區間連向包含的天。那麼我們每次加人,就相當於源點連向某個區間的邊的容量+1,這樣圖就只有 個點了。
#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;
}