$CSP\ 2019\ Day1$ 模拟考试 题解报告
\(CSP\ 2019\ Day1\) 模拟考试 题解报告
得分情况
\(T1\) \(100\ Pts\)
\(T2\) \(55\ Pts\)
\(T3\) \(0\ Pts\)
总分: \(155\ Pts\)
考试过程
读完题 先拿 \(T1\) 手摸样例半个多小时 上了个厕所出结论 过大样例 转 \(T2\) 看到有链的数据 搞了一个很诡异的思路 特判出来先把链的写了 然后转正解 捣鼓许久 (赛后发现自己的处理方法较为诡异 并不能通过) 小样例都能过 大样例爆栈被卡 反复调 过不去 弃掉 转 \(T3\) 读入不会处理 然后就没法写 凄惨爆零
题解
\(T1\) 格雷码
代码
/*
Time: 5.30
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int unsigned long long
/*--------------------------------------头文件*/
int n, k, 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;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
signed main() {
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
n = read(); k = read(); ans = k >> 1 ^ k;
for(int i = n; i; i--)
if(1ll << i - 1 & ans) putchar('1'); else putchar('0');
fclose(stdin);
fclose(stdout);
return 0;
}
/*
半个小时 上了个厕所 突然悟了
63 123321
000000000000000000000000000000000000000000000010001000101100101
悟了
k >> 1 ^ k
过大样例 开 ull
*/
\(T2\) 括号树
维护一个栈 遇到左括号直接入栈 栈中维护点标号 遇到右括号 且 栈中有东西 匹配成功一个 退栈 累加到来自父节点匹配成功的数量上 再统计贡献即可 退栈的时候记录一下退栈的点 由于是一棵树 在处理完一条链转到另一条链的时候要将多退的点再入栈
代码
/*
Time: 5.30
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define int long long
/*--------------------------------------头文件*/
const int B = 5e5 + 7;
const int C = 1e6 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, st[B], top, sum[B], ans, fa[B], lst[B];
char s[B];
struct edge {int v, nxt;} e[C];
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) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void dfs(int u) {
int tmp = 0;
if(s[u] == '(') st[++top] = u;
else if(top) tmp = st[top--], lst[u] = lst[fa[tmp]] + 1;
sum[u] = sum[fa[u]] + lst[u];
for(int i = head[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v) dfs(v);
if(tmp) st[++top] = tmp; else if(top) --top;
}
/*----------------------------------------函数*/
signed main() {
freopen("brackets.in","r",stdin);
freopen("brackets.out","w",stdout);
n = read(); scanf("%s", s + 1);
for(int i = 2; i <= n; i++)
{
int x = read(); fa[i] = x;
add_edge(x, i);
}
dfs(1);
for(int i = 1; i <= n; i++) ans ^= sum[i] * i;
printf("%lld", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
/*
5
(()()
1 1 2 2
10
()())()()))
1 2 2 1 5 6 6 6 6 9
深搜被卡
爆栈
大数据根本过不去
那个链的好像也会被卡
15 - 50
自己造了两组小样例倒是过了
弃
*/
\(T3\) 树上的数
\(10\ Pts\) 暴力
枚举删除每一条边的顺序 删完之后再扫一遍更新答案
代码
/*
Time: 6.2
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 2e3 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
}
/*----------------------------------------文件*/
int T, n, a[A], ans[A], tmp[A];
struct edge {int v, nxt;} e[A << 1];
int head[A], ecnt, lcnt;
std::pair <int, int> l[A];
bool vis[A];
/*------------------------------------变量定义*/
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) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void scan() {
n = read(); ecnt = 0; lcnt = 0; maxdu = 0;
for(int i = 1; i <= n; i++) a[read()] = i, head[i] = 0, du[i] = 0, ans[i] = n - i + 1;
for(int i = 1; i < n; i++)
{
int x = read(), y = read();
add_edge(x, y); add_edge(y, x); l[++lcnt] = std::make_pair(x, y);
}
}
void update() {
for(int i = 1; i <= n; i++) tmp[a[i]] = i;
for(int i = 1; i <= n; i++)
if(tmp[i] < ans[i]) {for(int j = 1; j <= n; j++) ans[j] = tmp[j]; break;}
else if(tmp[i] > ans[i]) break;
}
void dfs(int t) {
if(t == n) {update(); return ;}
for(int i = 1; i < n; i++) if(!vis[i])
{
Swap(a[l[i].first], a[l[i].second]); vis[i] = 1;
dfs(t + 1);
Swap(a[l[i].first], a[l[i].second]); vis[i] = 0;
}
}
void print() {for(int i = 1; i <= n; i++) printf("%d ", ans[i]); putchar('\n');}
void work() {
scan(); dfs(1); print();
}
/*----------------------------------------函数*/
int main() {
T = read(); while(T--) work();
return 0;
}
\(35\ Pts\) 暴力 + 菊花图
当删去一条边的时候 设花心为 \(x\) 点 边连接的另一点为 \(y\) 点 \(y\) 点的数字会来到花心 \(x\) 点的数字会到 \(y\) 点 且固定在 \(y\) 点 也就是说 除花心之外 其他点固定了删边时花心的值 而本身的数移动到花心 成为下一个固定的值
设花心为 \(u\) 其他点依次为 \(p_1, p_2, p_3, ..., p_i, p_{n - 1}\) \(a_i\) 表示 \(i\) 点上的数字 当依次删掉这些边的时候 有 \(a_{u} \to a_{p_1}, a_{p_1} \to a_{p_2}, ..., a_{p_{n - 1}} \to a_{p_n}, a_{p_n} \to a_u\) 相当于形成了一个环 环上每个点的数字向后顺延一位 上面的 \(p_i\) 可以是 \(u\) 点以外的任意点 我们可以贪心的构造这个环 依次枚举数字 再枚举点 为了使移动合法 最后出现的一定是一个大环 而不能提前出现小环 且用过的点不能再被用 并查集维护
代码
/*
Time: 6.2
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#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 A = 2e3 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
}
/*----------------------------------------文件*/
int T, n, a[A], maxdu, du[A], ans[A], tmp[A], fa[A], p[A];
struct edge {int v, nxt;} e[A << 1];
int head[A], ecnt, lcnt;
std::pair <int, int> l[A];
bool vis[A];
/*------------------------------------变量定义*/
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 find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void scan() {
n = read(); ecnt = 0; lcnt = 0; maxdu = 0;
for(int i = 1; i <= n; i++) a[read()] = i, head[i] = 0, du[i] = 0, ans[i] = n - i + 1;
for(int i = 1; i < n; i++)
{
int x = read(), y = read(); du[x]++; du[y]++;
add_edge(x, y); add_edge(y, x); l[++lcnt] = std::make_pair(x, y);
maxdu = Max(maxdu, Max(du[x], du[y]));
}
}
void update() {
for(int i = 1; i <= n; i++) tmp[a[i]] = i;
for(int i = 1; i <= n; i++)
if(tmp[i] < ans[i]) {for(int j = 1; j <= n; j++) ans[j] = tmp[j]; break;}
else if(tmp[i] > ans[i]) break;
}
void dfs(int t) {
if(t == n) {update(); return ;}
for(int i = 1; i < n; i++) if(!vis[i])
{
Swap(a[l[i].first], a[l[i].second]); vis[i] = 1;
dfs(t + 1);
Swap(a[l[i].first], a[l[i].second]); vis[i] = 0;
}
}
void solve1() {
for(int i = 1; i <= n; i++) tmp[a[i]] = i, fa[i] = i, vis[i] = 0;
for(int i = 1; i < n; i++) for(int j = 1; j <= n; j++) if(!vis[j] && find(tmp[i]) != find(j))
{
vis[j] = 1; p[tmp[i]] = j; fa[tmp[i]] = j;
break;
}
for(int i = 1; i <= n; i++) if(!vis[i]) {p[tmp[n]] = i; break;}
for(int i = 1; i <= n; i++) ans[i] = p[tmp[i]];
}
void print() {for(int i = 1; i <= n; i++) printf("%d ", ans[i]); putchar('\n');}
//i 点所存的数是 a[i]
//i 数所在的点是 tmp[i]
void work() {
scan();
if(n <= 10) dfs(1);
else if(maxdu == n - 1) solve1();
print();
}
/*----------------------------------------函数*/
int main() {
T = read(); while(T--) work();
return 0;
}
\(60\ Pts\) 暴力 + 菊花图 + 链
首先把这条链拎出来 从一段到另一端标号 一下默认点的编号从左到右递增
显然 每个数字只有移到左侧或移到右侧两种情况 注意这里说的是最终状态
考虑一个数字位于 \(x\) 点 这个数字的最终状态位于 \(y\) 点(\(y > x\)) 也就是说 \(y\) 点在 \(x\) 点右侧 那么 \(x\) 点右侧的边一定先于左侧的边被删除(否则 \(x\) 就跑左边回不来了) \(x\) 与 \(y\) 之间的边一定是由 \(x\) 到 \(y\) 依次删除 \(y\) 点右侧的边一定先于左侧被删除(否则 \(x\) 即使移到 \(y\) 点也会再被移走) 移到左侧的某个点同理
那么我们对于每个点维护一个标记 表示这个点是左侧的边先于右侧被删除(\(0\)) 还是右侧的边先于左侧被删除(\(1\)) 初始化为 \(2\)
从小到大逐一枚举每个数与每个点 如果一个点未被占有 尝试将这个数从本来的位置移动到枚举到的点 检查是否可以移动 如果可以 更新两点之间所有边的标记 并记录答案
/*
Time: 6.2
Worker: Blank_space
Source:
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#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 A = 2e3 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
inline void File() {
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
}
/*----------------------------------------文件*/
int T, n, a[A], maxdu, du[A], ans[A], tmp[A], fa[A], p[A], cnt, mark[A];
struct edge {int v, nxt;} e[A << 1];
int head[A], ecnt, lcnt;
std::pair <int, int> l[A];
bool vis[A];
/*------------------------------------变量定义*/
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 find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void scan() {
n = read(); ecnt = 0; lcnt = 0; maxdu = 0;
for(int i = 1; i <= n; i++) a[read()] = i, head[i] = 0, du[i] = 0, ans[i] = n - i + 1;
for(int i = 1; i < n; i++)
{
int x = read(), y = read(); du[x]++; du[y]++;
add_edge(x, y); add_edge(y, x); l[++lcnt] = std::make_pair(x, y);
maxdu = Max(maxdu, Max(du[x], du[y]));
}
}
void update() {
for(int i = 1; i <= n; i++) tmp[a[i]] = i;
for(int i = 1; i <= n; i++)
if(tmp[i] < ans[i]) {for(int j = 1; j <= n; j++) ans[j] = tmp[j]; break;}
else if(tmp[i] > ans[i]) break;
}
void dfs(int t) {
if(t == n) {update(); return ;}
for(int i = 1; i < n; i++) if(!vis[i])
{
Swap(a[l[i].first], a[l[i].second]); vis[i] = 1;
dfs(t + 1);
Swap(a[l[i].first], a[l[i].second]); vis[i] = 0;
}
}
void solve1() {
for(int i = 1; i <= n; i++) tmp[a[i]] = i, fa[i] = i, vis[i] = 0;
for(int i = 1; i < n; i++) for(int j = 1; j <= n; j++) if(!vis[j] && find(tmp[i]) != find(j))
{
vis[j] = 1; p[tmp[i]] = j; fa[tmp[i]] = j;
break;
}
for(int i = 1; i <= n; i++) if(!vis[i]) {p[tmp[n]] = i; break;}
for(int i = 1; i <= n; i++) ans[i] = p[tmp[i]];
}
void dfs2(int u, int pre) {
p[u] = ++cnt;
for(int i = head[u], v = e[i].v; i; i = e[i].nxt, v = e[i].v) if(v != pre) dfs2(v, u);
}
bool check(int x, int y, int k) {
int u = Min(p[x], p[y]), v = Max(p[x], p[y]);
if(mark[p[x]] == (k ^ 1)) return 0;
for(int i = u + 1; i <= v - 1; i++) if(mark[i] == k) return 0;
if(mark[p[y]] == (k ^ 1)) return 0;
return 1;
}
void up_date(int x, int y, int k) {
int u = Min(p[x], p[y]), v = Max(p[x], p[y]);
if(p[x] != 1 && p[x] != n) mark[p[x]] = k;
for(int i = u + 1; i <= v - 1; i++) mark[i] = k ^ 1;
if(p[y] != 1 && p[y] != n) mark[p[y]] = k;
}
void solve2() {
cnt = 0;
for(int i = 1; i <= n; i++) if(du[i] == 1) {dfs2(i, 0); break;}
for(int i = 1; i <= n; i++) tmp[a[i]] = i, mark[i] = 2, vis[i] = 0;
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(!vis[j] && p[tmp[i]] != p[j])
{
bool flag = 0;
if(p[tmp[i]] < p[j]) {if(check(tmp[i], j, 1)) up_date(tmp[i], j, 1), flag = 1;}
else {if(check(tmp[i], j, 0)) up_date(tmp[i], j, 0), flag = 1;}
if(flag) ans[i] = j, vis[j] = 1;
}
}
void print() {for(int i = 1; i <= n; i++) printf("%d ", ans[i]); putchar('\n');}
//i 点所存的数是 a[i]
//i 数所在的点是 tmp[i]
void work() {
scan();
if(n <= 10) dfs(1);
else if(maxdu == n - 1) solve1();
else if(maxdu == 2) solve2();
print();
}
/*----------------------------------------函数*/
int main() {
T = read(); while(T--) work();
return 0;
}
\(100\ Pts\) 正解
咕了