\(T1\) \(100\ Pts\)
\(T2\) \(55\ Pts\)
\(T3\) \(0\ Pts\)
总分: \(155\ Pts\)
读完题 先拿 \(T1\) 手摸样例半个多小时 上了个厕所出结论 过大样例 转 \(T2\) 看到有链的数据 搞了一个很诡异的思路 特判出来先把链的写了 然后转正解 捣鼓许久 (赛后发现自己的处理方法较为诡异 并不能通过) 小样例都能过 大样例爆栈被卡 反复调 过不去 弃掉 转 \(T3\) 读入不会处理 然后就没法写 凄惨爆零
代码
/* 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 */
维护一个栈 遇到左括号直接入栈 栈中维护点标号 遇到右括号 且 栈中有东西 匹配成功一个 退栈 累加到来自父节点匹配成功的数量上 再统计贡献即可 退栈的时候记录一下退栈的点 由于是一棵树 在处理完一条链转到另一条链的时候要将多退的点再入栈
代码
/* 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 自己造了两组小样例倒是过了 弃 */
\(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\) 正解
咕了