题目在这
【复健不太顺利 www
【有没有一种可能是一直都是这样的水平 w
【导致复健并没有什么用 w
题意:有三把钥匙三扇门,三扇门都上了锁,其中两扇门后面藏着钥匙,另一扇门后面没有钥匙,以及另一把钥匙在你手上。钥匙的 id 对应了门的 id 。现在告诉你你手里拿了哪把钥匙,以及三扇门后面放了哪把钥匙(没有就是 0 ),问能不能把三扇门都打开。
做法:开了门如果一直有钥匙就可以,如果有扇门开开发现没钥匙就 gg 。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; int n, a[5]; void Main(){ scanf("%d", &n); rep (i,1,3) scanf("%d", &a[i]); rep (i,1,2){ n = a[n]; if (!n){puts("NO"); return;} } puts("YES"); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }View Code
题意:有 n 列,每列有个高度 $h_i$ ,从高度 x 飞到高度 y,如果 $x > y$ ,就得到 $x-y$ 的 damage 值,否则得到 $0$ 的 damage 值。现在有 $m$ 个询问每次问从 $l$ 飞到 $r$ 最少得到多少 damage 值。(必须一列一列飞,$l$ 可以 $>r$)
做法:前缀和。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 1e5+10; int n, Q, a[N]; ll b[N], c[N]; int main(){ scanf("%d%d", &n, &Q); rep (i,1,n){ scanf("%d", &a[i]); if (i>1 && a[i]<a[i-1]) b[i] = a[i-1] - a[i]; else b[i] = 0; } rep (i,1,n) b[i] += b[i-1]; for (int i=n-1; i>=1; i--){ if (a[i]<a[i+1]) c[i] = a[i+1] - a[i]; c[i] += c[i+1]; } while (Q--){ int l, r; scanf("%d%d", &l, &r); if (l<r){ printf("%lld\n", b[r]-b[l]); } else{ printf("%lld\n", c[r]-c[l]); } } return 0; }View Code
题意:有一个合法的括号序列,其中某些位置是 '?' ,问存不存在一种唯一的填充 '?' 的方法使其成为一个合法的括号序列。
做法:首先根据问号数量以及序列中已有的左括号数和右括号数,可以算出 '?' 中的左括号数 x 和右括号数 y 。显然如果 $x=0\ or\ y = 0$ 方法肯定唯一。否则一定有一种满足要求的方法是前面 x 个问号都放 '(' ,后面 y 个放 ')' 。因为左括号放前面肯定更优。那么我们只要检查一下前面放 x-1 个 '(' 然后放一个 ')(' ,再放 y-1 个 ')' 是否可行就好了,如果这样不可行说明存在唯一解。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) int read(){ char ch = getchar(); int x = 0, op = 1; for (; !isdigit(ch); ch = getchar()) if (ch == '-') op = -1; for (; isdigit(ch); ch = getchar()) x = (x<<1)+(x<<3)+ch-'0'; return x * op; } void write(int a){ if (a<0) putchar('-'), a=-a; if (a>=0) write(a/10); putchar(a%10+'0'); } const int N = 2e5+10; int n, pos[N]; char s[N]; bool check(char *s){ int tmp = 0; rep (i,1,n){ if (s[i]=='(') tmp ++; else tmp --; if (tmp<0) return 1; } return 0; } void Main(){ scanf("%s", s+1); n=strlen(s+1); int X = 0, Y = 0; rep (i,1,n){ if (s[i] == '?') X ++, pos[X] = i; else if (s[i] == '(') Y --; else Y ++; } if ((X+Y)%2 != 0){puts("NO"); return;} int x = (X + Y)/2, y = (X - Y)/2; if (!x || !y){puts("YES"); return;} rep (i,1,x-1) s[pos[i]] = '('; s[pos[x]] = ')'; s[pos[x+1]] = '('; rep (i,x+2,x+y) s[pos[i]] = ')'; if (check(s)) puts("YES"); else puts("NO"); return; } int main(){ int T = read(); while (T --) Main(); return 0; }View Code
题意:有一个 n*m 的矩阵,第 $i$ 列从下往上有 $a_i$ 个格子是障碍格,其余是空地。现在有一个机器人每次必须走 k 步,有 q 个询问,每次给出机器人的起始格子和终止格子以及 k ,问机器人能不能恰好从起点走到终点(不经过障碍格)。
做法:首先可以发现起始格子和终止格子横纵坐标的差的绝对值都应该是 k 的倍数。其次最优的走法肯定是,从起始格子往上走每次走 k 步走到最上面,然后往左右走,再往下走。那么这个时候就判断一下起始格子和终止格子的区间内是不是有一个列,它的障碍格的高度,比机器人能走到的最高高度还要高,如果还要高的话就没法走到。区间 max 用 ST 表实现。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 2e5+10; int n, m, Q, h[N], f[N][22], Log[N]; int qry_max(int l, int r){ int t = Log[r-l+1]; return max(f[l][t], f[r-(1<<t)+1][t]); } int main(){ scanf("%d%d", &n, &m); Log[0] = -1; rep (i,1,m) Log[i] = Log[i>>1] + 1; rep (i,1,m){ scanf("%d", &h[i]); f[i][0] = h[i]; } rep (j,1,19) for (int i=1; i+(1<<j)-1<=m; i++) f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]); scanf("%d", &Q); while (Q--){ int sx, sy, tx, ty, step; scanf("%d%d%d%d%d", &sx, &sy, &tx, &ty, &step); if (sy > ty) swap(sy, ty), swap(sx, tx); if (abs(sx - tx) % step != 0 || abs(sy - ty) % step != 0){puts("NO"); continue;} int H = (n-sx)/step * step + sx; if (H <= qry_max(sy, ty)) puts("NO"); else puts("YES"); } return 0; }View Code
题意:给一棵树,每个点有权值 $a_i$ ,现在希望每条链权值的异或和都不为 0 ,问最少要改动几个点的权值。
做法:首先注意到我们可以改成任意权值,如果改动一个点,我们可以将它改成 $2^{30+x}$ ,这样改动之后任意经过它的路径都不可能异或和为 0 了。其次,考虑保存一个 $val_i$ 表示 $i$ 到根节点的权值异或和,那么一条路径 u ~ v 的权值异或和就是 $val_u \oplus val_v \oplus a_{lca(u,v)}$ 。我们考虑每个节点开一个 set ,保存所有子树中没给节点的 $val$ 值,然后对一个 $u$ ,对其所有儿子的 $set$ 进行启发式合并,合并的同时判断是否有 $val_u \oplus val_v \oplus a_{lca(u,v)} = 0$ 的情况出现,如果有了的话将答案 +1 ,同时 u 节点的 set 也可以清零,不参与之后的统计。复杂度是 $O(n\log^2n)$ 。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long #define pii pair<int, int> using namespace std; int read(){ char ch = getchar(); int x = 0, op = 1; for (; !isdigit(ch); ch = getchar()) if (ch == '-') op = -1; for (; isdigit(ch); ch = getchar()) x = (x<<1)+(x<<3) + ch - '0'; return x*op; } void write(int a){ if (a<0) a=-a, putchar('-'); if (a>=10) write(a/10); putchar(a%10+'0'); } const int N = 2e5+10; int n, a[N], cnt, head[N], val[N], ans; set<int> s[N]; struct edge{ int to, nxt; }e[N<<1]; void adde(int x,int y){ e[++ cnt].to = y; e[cnt].nxt = head[x]; head[x] = cnt; } void pre(int u, int fa){ val[u] = val[fa] ^ a[u]; for (int i=head[u], v; i; i = e[i].nxt) if ((v=e[i].to)!=fa) pre(v, u); } void dfs(int u, int fa){ s[u].insert(val[u]); int flag = 0; for (int i=head[u], v; i; i = e[i].nxt) if ((v=e[i].to) != fa){ dfs(v, u); if ((int)s[v].size() > (int)s[u].size()) s[u].swap(s[v]); for (auto t:s[v]) if (s[u].count(t ^ a[u])) flag = 1; for (auto t:s[v]) s[u].insert(t); s[v].clear(); } if (flag){ s[u].clear(); ans ++; } } int main(){ n = read(); rep (i,1,n) a[i] = read(); rep (i,1,n-1){ int x = read(), y = read(); adde(x, y); adde(y, x); } pre(1, 0); dfs(1, 0); write(ans); return 0; }View Code
F 咕咕