[HNOI2013]题解
代码在最后
[HNOI2013]比赛
记忆化搜索
把每一位还需要多少分用\(27\)进制压进\(long\) \(long\),\(map\)记忆化一下即可
[HNOI2013]消毒
先考虑在二维平面
问题就是最小点覆盖
最小点覆盖 = 二分图最大匹配
对于每个点\((x,y)\),\(x\)连向\(y\)一条边,然后跑最大匹配
扩展到三维
好像不太好搞....
\(a*b*c<=5000\)
我们把\(a\)调换成\(min(a, b, c)\)
\(a\)最大为\(\sqrt[3]{5000}\)约\(17\)
对于每一层,要么和上面一起消,要么一次消掉这一层
枚举每层的状态
然后再跑最大匹配\(O(2^{17}n^3)\)
[HNOI2013]旅行
构造+单调队列
题目大意:给定\(1,-1\)组成的序列,分成\(m\)段,使得\(m\)段绝对值最大值最小
设:
后缀和为\(sum[i]\),后缀和为\(0\)的个数为\(cnt[i]\),序列和为\(S\)
然后分类讨论
-
\(S = 0\)
\(1.\)若\(cnt[1] \ge m\),则\(ans = 0\).直接选\(0\)的位置即可
维护一个单调队列
\(2.\) 若\(cnt[1]<m\), 则\(ans = 1\)
构造方法与 \(S \neq 0\) 一样
-
\(S \neq 0\)
\(ans = \lceil\frac{|S|}{m}\rceil\)
因为区间和为\(0\)的可以消掉,剩下的均分最小
现在问题是如何求出字典序最小的方案
假设上一个休息点已经确定为\(last\),当前决策到第\(i\)个休息点
那么新的休息点(第i个休息点)$$a$需要满足:
- \(n - a \ge m-i\)
- \(\lceil \frac{|sum[a + 1]|}{m - i}\rceil \leq ans\)
- \(|sum[last+1]-sum[a+1]| \leq ans\)
好像不太好搞,考虑暴力
我们可以对于每个每种\(sum\)开一个单调队列
每次暴力从\([sum[last+1]-ans, sum[last+1]+ans]\) 取出合法的放进答案队列
再维护答案队列的单调性,保证答案字典序最小
复杂度:\(O(m\lceil \frac{|S|}{m} \rceil) = O(|S|)\)
[HNOI2013]数列
\(20\%\):\(dp\)
\(f[i][j]\)前\(i\)位,已经分了\(j\)份
转移用前缀和优化一下
\(O(nk)\)
\(100\%\):
先写出式子:
设差分数组为:\(a_1, a_2, a_3, ..., a_{k-1}\)
\(\sum^{m}_{a_1 = 1}\sum^{m}_{a_2 = 1}\sum^{m}_{a_3 = 1}...\sum^{m}_{a_{k-1} = 1}(n-\sum_{i=1}^{k-1}a_i)\)
\(= n * m^{k-1} - \sum^{m}_{a_1 = 1}\sum^{m}_{a_2 = 1}\sum^{m}_{a_3 = 1}...\sum^{m}_{a_{k-1} = 1}\sum_{i=1}^{k-1}a_i\)
\(=n*m^{k-1}-\sum_{i=1}^{k-1}\sum_{a_i=1}^m*m^{k-2}\)
\(=n*m^{k-1}-(k-1)*m^{k-2}*\frac{(m+1)m}{2}\)
\(快速幂即可\)
- 注意取模
[HNOI2013]游走
题面要求算边的期望
如果求出每条边期望经过次数,就可以求出答案
每条边经过次数,显然需要求出两端点期望经过次数
设点\(i\)期望次数为\(f[i]\)
\(deg[i]\)为\(i\)的入度
对于一个点\(i\)
存在边\(x-i\)
\(f[i] = \sum \frac{f[x]}{deg[x]}\)
\(f[1]\)要加\(1\),因为一开始在\(1\)
\(f[n]\)不要算,因为到了\(n\)就停止
这个东西列个方程,高斯消元求
然后算出每条边经过次数的期望
对于\(x-y\)
\(q[i] = f[x] / deg[x] + f[y] / deg[y]\)
显然经过次数多的边,赋值小
排序弄一下即可
[HNOI2013]切糕
最小割
题目其实就是求最小的代价使得每个纵轴被分成两部分
我们把每个点抽象成一条边,一个纵轴就是一条\(S−T\)的路径
但是题目要求\(|f(x,y)−f(x′,y′)|≤D\)
不能直接跑最小割
考虑如何限制
首先,\(|f(x,y)−f(x′,y′)|≤D\)是相互的
所以只要考虑 \(f(x,y)−f(x′,y′)≤D\)
那么对于\((x, y, z)\) 我们向它相邻列的第\(x-D\)层连一条\(inf\)边
贪心想一下,这样一定保证超过限制的两个点不会同时割掉
然后代码就很简单啦
Code
[HNOI2013]比赛
#include<bits/stdc++.h>
#define LL unsigned long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 15, Mod = 1e9 + 7;
int n, s[N], ans, a[N], b[N], cnt1, cnt2;
map<LL, LL> M;
bool cmp(int x, int y) {
return x > y;
}
LL dfs(int x, int y) {
if (a[x] + (n - y + 1) * 3 < s[x]) return 0;
if (x == n) return 1;
if (y > n) {
for (int i = x + 1; i <= n; i++) b[i] = s[i] - a[i];
sort(b + 1 + x, b + n + 1, cmp);
LL S = n - x;
for (int i = x + 1; i <= n; i++) S = S * 27 + b[i];
if (M.find(S) != M.end()) return M[S];
else return M[S] = dfs(x + 1, x + 2);
}
LL res = 0;
if (a[x] + 3 <= s[x] && cnt1) {
a[x] += 3;
cnt1--;
(res += dfs(x, y + 1)) %= Mod;
a[x] -= 3;
cnt1++;
}
if (a[x] < s[x] && a[y] < s[y] && cnt2) {
a[x]++; a[y]++;
cnt2--;
(res += dfs(x, y + 1)) %= Mod;
a[x]--; a[y]--;
cnt2++;
}
if (a[y] + 3 <= s[y] && cnt1) {
a[y] += 3;
cnt1--;
(res += dfs(x, y + 1)) %= Mod;
a[y] -= 3;
cnt1++;
}
return res;
}
int main() {
read(n);
int sum;
for (int i = 1; i <= n; i++)
read(s[i]), sum += s[i];
sort(s+1, s+1+n, cmp);
cnt1 = sum - n * (n - 1);
cnt2 = (sum - cnt1 * 3) / 2;
printf("%lld\n", dfs(1, 2));
return 0;
}
[HNOI2013]消毒
#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 5010;
int a, b, c;
int q[3][N], ql, ans;
void init() {
ql = 0;
read(a), read(b), read(c);
int mn = min(a, min(b, c));
for (int i = 1; i <= a; i++)
for (int j = 1; j <= b; j++)
for (int k = 1; k <= c; k++) {
int x; read(x);
if (x == 1)
q[0][++ql] = i, q[1][ql] = j, q[2][ql] = k;
}
if (mn == b) {
swap(a, b); swap(q[0], q[1]);
}
else if (mn == c) {
swap(a, c); swap(q[0], q[2]);
}
return ;
}
bool flag[N], used[N];
int match[N];
struct node {
int to, nxt;
}g[N];
int last[N], gl;
void add(int x, int y) {
g[++gl] = (node) {y, last[x]};
last[x] = gl;
}
bool dfs(int u) {
for (int i = last[u]; i; i = g[i].nxt) {
int v = g[i].to;
if (used[v]) continue;
used[v] = 1;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return 1;
}
}
return 0;
}
void work(int S) {
int res = 0;
for (int i = 0; i < a; i++)
if (S & (1 << i))
flag[i + 1] = 1, res++;
else flag[i + 1] = 0;
memset(last, 0, sizeof(last)); gl = 0;
memset(match, 0, sizeof(match));
for (int i = 1; i <= ql; i++)
if (!flag[q[0][i]])
add(q[1][i], q[2][i]);
for (int i = 1; i <= b; i++) {
for (int j = 1; j <= c; j++) used[j] = 0;
if (dfs(i)) res++;
}
ans = min(ans, res);
return ;
}
int main() {
int T;
read(T);
while (T--) {
init();
ans = 2147483647;
for (int i = 0; i < (1 << a); i++)
work(i);
printf("%d\n", ans);
}
return 0;
}
[HNOI2013]旅行
#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 5e5 + 5;
int n, m, a[N], sum[N], cnt[N];
struct Node { int l, r, v; } Line[N << 1]; int tot = 0;
struct deque {
int head, tail, len;
bool empty() { return !len; }
int newNode(int l, int r, int v) { Line[++tot] = (Node){ l, r, v }; return tot; }
int front() { return Line[head].v; }
int back() { return Line[tail].v; }
void pop_back() { tail = Line[tail].l, len--; }
void pop_front() { head = Line[head].r, len--; }
void push_back(int v) {
if (!len) head = tail = newNode(0, 0, v);
else Line[tail].r = newNode(tail, 0, v), tail = Line[tail].r;
len++;
}
void push(int v) {
while (len && a[back()] > a[v]) pop_back();
push_back(v);
}
} Q[N << 1], Qu[N << 1], *q = Q + N, *qu = Qu + N;
#define min(x, y) ((a[x]) < (a[y]) ? (x) : (y))
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
read(a[i]), read(sum[i]), sum[i] = sum[i] ? 1 : -1;
for (int i = n - 1; i >= 1; i--) sum[i] += sum[i + 1];
for (int i = n; i >= 1; i--) cnt[i] = cnt[i + 1] + (!sum[i]);
int S = sum[1], d = S ? ceil(1.0 * abs(S) / m) : cnt[1] < m;
cnt[n + 1] = -1;
if (!d) {
for (int i = 1, j = 2; i < m; i++) {
while (cnt[j + 1] >= m - i) {
if (!sum[j + 1]) q[0].push(j);
j++;
}
printf("%d ", a[q[0].front()]);
q[0].pop_front();
}
}
else {
for (int i = 2; i <= n; i++) qu[sum[i]].push_back(i - 1);
int last = 0;
a[n + 1] = n + 1;
for (int i = 1; i < m; i++) {
int ans = n + 1;
for (int j = sum[last + 1] - d; j <= sum[last + 1] + d; j++) {
if (ceil(1.0 * abs(j) / (m - i)) > d) continue;
while (!qu[j].empty() && n - qu[j].front() >= m - i) {
if (qu[j].front() > last) q[j].push(qu[j].front());
qu[j].pop_front();
}
while (!q[j].empty() && q[j].front() <= last)
q[j].pop_front();
if (!q[j].empty()) ans = min(ans, q[j].front());
}
last = ans;
printf("%d ", a[ans]);
}
}
printf("%d\n", a[n]);
return 0;
}
[HNOI2013]数列
#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
LL n, k, m, p;
LL fastpow(LL a, LL b) {
LL res = 1;
for (; b; b >>= 1, a = a * a % p) if (b & 1) res = res * a % p;
return res;
}
int main() {
//freopen(".in", "r", stdin);
//freopen(".out", "w", stdout);
read(n), read(k), read(m), read(p);
LL ans = (n % p * fastpow(m, k - 1) % p - (k - 1) * fastpow(m, k - 2) % p * ((m + 1) * m / 2 % p) % p + p) % p;
write(ans);
return 0;
}
[HNOI2013]游走
#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 510;
struct node {
int to, nxt;
}g[N * 1000];
int last[N], gl, deg[N], n, m;
void add(int x, int y) {
g[++gl] = (node) {y, last[x]};
last[x] = gl;
g[++gl] = (node) {x, last[y]};
last[y] = gl;
deg[y]++; deg[x]++;
}
double a[N][N];
void Gauss() {
for (int i = 1, k; i < n; i++) {
k = i;
for (int j = i + 1; j < n; j++)
if (fabs(a[k][i]) < fabs(a[j][i])) k = j;
if (k != i) swap(a[k], a[i]);
for (int j = i + 1; j < n; j++)
for (int k = n; k >= i; k--)
a[j][k] -= a[i][k] * a[j][i] / a[i][i];
}
for (int i = n - 1; i; i--) {
for (int j = i + 1; j < n; j++)
a[i][n] -= a[i][j] * a[j][n];
a[i][n] /= a[i][i];
}
return ;
}
double q[N * N];
int X[N * N], Y[N * N];
int main() {
read(n); read(m);
for (int i = 1, x, y; i <= m; i++) {
read(x), read(y);
add(x, y);
X[i] = x, Y[i] = y;
}
for (int x = 1; x < n; x++) {
a[x][x] = 1;
for (int i = last[x]; i; i = g[i].nxt) {
int v = g[i].to;
if (v == n) continue;
a[x][v] = -1.0 / deg[v];
}
}
a[1][n] = 1;
Gauss();
for (int i = 1; i <= m; i++)
q[i] = a[X[i]][n] / deg[X[i]] + a[Y[i]][n] / deg[Y[i]];
sort(q + 1, q + 1 + m);
double ans = 0;
for (int i = 1; i <= m; i++)
ans += q[i] * (m - i + 1);
printf("%.3lf\n", ans);
return 0;
}
[HNOI2013]切糕
#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;
template<class T> inline void read(T &x) {
x = 0; RG char c = getchar(); bool f = 0;
while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
x = f ? -x : x;
return ;
}
template<class T> inline void write(T x) {
if (!x) {putchar(48);return ;}
if (x < 0) x = -x, putchar('-');
int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 80000, inf = 2147483647;
struct node {
int to, nxt, w;
}g[2000000];
int last[N], gl = 1;
void add(int x, int y, int z) {
g[++gl] = (node) {y, last[x], z};
last[x] = gl;
g[++gl] = (node) {x, last[y], 0};
last[y] = gl;
}
queue<int> q;
int dep[N], s, t, cur[N];
bool bfs() {
memset(dep, 0, sizeof(dep));
dep[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = last[u]; i; i = g[i].nxt) {
int v = g[i].to;
if (!dep[v] && g[i].w) {
dep[v] = dep[u]+1;
q.push(v);
}
}
}
return dep[t] == 0 ? 0 : 1;
}
int dfs(int u, int d) {
if (u == t) return d;
for (int &i = cur[u]; i; i = g[i].nxt) {
int v = g[i].to;
if (dep[v] == dep[u]+1 && g[i].w) {
int di = dfs(v, min(d, g[i].w));
if (di) {
g[i].w -= di;
g[i^1].w += di;
return di;
}
}
}
return 0;
}
int Dinic() {
int ans = 0;
while (bfs()) {
for (int i = 1; i <= t; i++) cur[i] = last[i];
while (int d = dfs(s, inf)) ans += d;
}
return ans;
}
int a[50][50][50], id[50][50][50];
int fx[] = {0, 1, -1, 0};
int fy[] = {1, 0, 0, -1};
int main() {
int p, q, r, d, tot = 0;
read(p), read(q), read(r), read(d);
for (int i = 1; i <= r; i++)
for (int j = 1; j <= p; j++)
for (int k = 1; k <= q; k++)
read(a[i][j][k]), id[i][j][k] = ++tot;
for (int j = 1; j <= p; j++)
for (int k = 1; k <= q; k++)
id[r+1][j][k] = ++tot;
s = tot+1, t = s+1;
for (int i = 1; i <= p; i++)
for (int j = 1; j <= q; j++)
add(s, id[1][i][j], inf), add(id[r+1][i][j], t, inf);
for (int k = 1; k <= r; k++)
for (int i = 1; i <= p; i++)
for (int j = 1; j <= q; j++)
add(id[k][i][j], id[k+1][i][j], a[k][i][j]);
for (int k = d+1; k <= r+1; k++)
for (int i = 1; i <= p; i++)
for (int j = 1; j <= q; j++) {
for (int z = 0; z < 4; z++) {
int x = i + fx[z], y = j + fy[z];
if (x < 1 || y < 1 || x > p || y > q) continue;
add(id[k][i][j], id[k-d][x][y], inf);
}
}
printf("%d\n", Dinic());
return 0;
}