$NOIP\ 2015\ Day2$ 模拟考试 题解报告
\(NOIP\ 2015\ Day2\) 模拟考试 题解报告
得分情况
\(T1\ 100\ Pts\)
\(T2\ 90\ Pts\)
\(T3\ 15\ Pts\)
总分: \(205\ Pts\)
考试过程
太久了 快忘了...
\(T1\) 用时比较短 半个小时左右 感觉可以了写 \(T2\)
\(T2\) 一看就是 \(DP\) 写了半个小时 调了半个多小时 然后看 \(T3\)
\(T3\) 先写的只有一条的时候 然后写链 写完之后查的时候发现写错了 然后调 然后 没调过来
题解
\(T1\) 跳石头
二分答案 注意一下边界
代码
/*
Time: 6.20
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
const int A = 5e4 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen("stone.in", "r", stdin);
freopen("stone.out", "w", stdout);
}
/*----------------------------------------文件*/
int L, n, m, a[A], ans;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
bool check(int x) {
int cnt = 0, pos = 0;
for(int i = 1; i <= n + 1; i++)
{
if(a[i] - a[pos] < x) cnt++;
else pos = i;
}
return cnt <= m;
}
/*----------------------------------------函数*/
int main() {
File();
L = read(); n = read(); m = read(); a[n + 1] = L;
for(int i = 1; i <= n; i++) a[i] = read();
int l = 0, r = L;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
printf("%d", ans);
return 0;
}
\(T2\) 子串
状态: \(f_{i, j, k, 0/1}\) 表示考虑 \(a\) 串前 \(i\) 个 \(b\) 串前 \(j\) 个 已经取了 \(k\) 个串 当前位置取不取 的方案数
转移 不取的话直接继承 能取的话可以划分到上一个位置或自己再开一个串 累加方案数
注意滚动数组空间优化 注意滚动数组的清空 注意初始化
代码
/*
Time: 6.20
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen("substring.in", "r", stdin);
freopen("substring.out", "w", stdout);
}
/*----------------------------------------文件*/
int n, m, l, f[2][210][210][2], sum;
char a[1010], b[210];
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main() {
File();
n = read(); m = read(); l = read();
scanf("%s%s", a + 1, b + 1);
for(int i = 1; i <= n; i++)
{
memset(f[i & 1], 0, sizeof f[i & 1]);
f[i & 1][1][1][0] = sum; if(a[i] == b[1]) f[i & 1][1][1][1] = 1, sum++;
for(int j = 2; j <= m; j++) for(int k = 1; k <= l; k++)
{
f[i & 1][j][k][0] = (f[i & 1][j][k][0] + f[i & 1 ^ 1][j][k][0] + f[i & 1 ^ 1][j][k][1]) % mod;
if(a[i] == b[j]) f[i & 1][j][k][1] = ((f[i & 1][j][k][1] + f[i & 1 ^ 1][j - 1][k - 1][0] + f[i & 1 ^ 1][j - 1][k - 1][1]) % mod + f[i & 1 ^ 1][j - 1][k][1]) % mod;
}
}
printf("%d", (f[n & 1][m][l][0] + f[n & 1][m][l][1]) % mod);
return 0;
}
\(T3\) 运输计划
时间取决于最长的一条路径 所以题目相当于在搞掉一条边之后使最长的一条路径最短
可能从这里可以看出二分答案吧
二分最大时间 检查所有路径 对于超过二分出的时间的路径通过差分进行统计 找出所有路径的交 从交中找出最长的一条边 不难发现 这条边一定在最长的一条路径上 每一次也只需要用最长的一条路径减去这条边 判断是否在二分出的时间内 所以需要预先求出最长的路径的长度 且需要多次用到点对之间的 \(LCA\) 可以提前预处理
代码
/*
Time: 6.23
Worker: Blank_space
Source: P2680 [NOIP2015 提高组] 运输计划
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define mid (l + r >> 1)
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int B = 3e5 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
}
/*----------------------------------------文件*/
int n, m, dis[B], dep[B], top[B], fa[B], siz[B], son[B], max, c[B], d[B], cnt, _max, ans;
struct node {int u, v, lca, w;} a[B];
struct edge {int v, w, nxt;} e[B << 1];
int head[B], ecnt;
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
void dfs1(int u, int pre) {
siz[u] = 1; fa[u] = pre; dep[u] = dep[pre] + 1;
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].v, w = e[i].w; if(v == pre) continue;
d[v] = w; dis[v] = dis[u] + w; dfs1(v, u); siz[u] += siz[v];
if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp; if(son[u]) dfs2(son[u], tp);
for(int i = head[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v)
if(v != fa[u] && v != son[u]) dfs2(v, v);
}
int LCA(int x, int y) {
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) Swap(x, y);
x = fa[top[x]];
}
if(dep[x] > dep[y]) Swap(x, y);
return x;
}
void dfs(int u, int pre) {
for(int i = head[u]; i; i = e[i].nxt) if(e[i].v != pre) dfs(e[i].v, u), c[u] += c[e[i].v];
if(c[u] == cnt && d[u] > _max) _max = d[u];
}
bool check(int x) {
memset(c, 0, sizeof c); cnt = _max = 0;
for(int i = 1; i <= m; i++)
if(a[i].w > x) c[a[i].u]++, c[a[i].v]++, c[a[i].lca] -= 2, cnt++;
dfs(1, 0); return max - _max <= x;
}
/*----------------------------------------函数*/
int main() {
n = read(); m = read();
for(int i = 1; i < n; i++)
{
int x = read(), y = read(), z = read();
add_edge(x, y, z); add_edge(y, x, z);
}
dfs1(1, 0); dfs2(1, 1);
for(int i = 1; i <= m; i++)
{
int x = read(), y = read(), z = LCA(x, y), k = dis[x] + dis[y] - 2 * dis[z];
a[i] = (node){x, y, z, k}; max = Max(max, k);
}
int l = 0, r = max;
while(l <= r)
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
printf("%d", ans);
return 0;
}
很罕见的调都没调 写完一发 \(A\) 了