2022NOIPA层联测7
问题 A: 【2022NOIP联测710月11日】找(a)
一看到是个数学题还感觉挺恐怖,把式子写出来才发现它很水。
没开long long大样例跑不出来还以为T1又没了……然而幸好及时发现问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 8; const ll mod = 998244353; int n; ll suma, sumb, sx1, sx2, fa[maxn], fb[maxn], ans, a[maxn], b[maxn]; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } int main() { n = read(); for(int i=1; i<=n; i++) { a[i] = read(); b[i] = read(); fa[i] = a[i]*a[i]%mod; fb[i] = b[i]*b[i]%mod; suma = (suma + a[i]) % mod; sumb = (sumb + b[i]) % mod; } for(int i=1; i<=n; i++) { sx1 = (sx1 + fa[i]) % mod; sx2 = (sx2 + fb[i]) % mod; } for(int i=1; i<=n; i++) { ans = ((n-2)*fa[i]%mod + (n-2)*fb[i]%mod) % mod; ans = (ans + sx1 + sx2) % mod; ll del = (2*a[i]%mod*(suma-a[i]+mod)%mod+2*b[i]%mod*(sumb-b[i]+mod)%mod)%mod; ans = (ans+mod-del)%mod; printf("%lld\n", ans); } return 0; }
问题 B: 【2022NOIP联测710月11日】女(b)
第一版暴力是把所有黑点的集合记下来,对每一个是黑点的点求距离,找距离最小的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const int inf = 0x3f3f3f3f; int n, q, dep[maxn], siz[maxn], son[maxn], fa[maxn], top[maxn]; set<int> s; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }a[maxn<<1]; int head[maxn], len; void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } void find_heavy_edge(int u, int fat, int depth) { dep[u] = depth; fa[u] = fat; son[u] = 0; siz[u] = 1; int maxsize = 9; for(int i=head[u]; i; i=a[i].next) { int v = a[i].to; if(dep[v]) continue; find_heavy_edge(v, u, depth+1); siz[u] += siz[v]; if(siz[v] > maxsize) { maxsize = siz[v]; son[u] = v; } } } void connect_heavy_edge(int u, int ancestor) { top[u] = ancestor; if(son[u]) { connect_heavy_edge(son[u], ancestor); } for(int i=head[u]; i; i=a[i].next) { int v = a[i].to; if(v == fa[u] || v == son[u]) continue; connect_heavy_edge(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; } int main() { n = read(); q = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } find_heavy_edge(1, 1, 1); connect_heavy_edge(1, 1); while(q--) { int opt = read(); if(opt == 1) { int x = read(); if(s.find(x) != s.end()) s.erase(x); else s.insert(x); } else { int x = read(); if(s.empty()) {printf("-1\n"); continue;} int ans = inf; for(int y : s) { ans = min(ans, dep[x]+dep[y]-2*dep[LCA(x,y)]); } printf("%d\n", ans); } } return 0; }
比较优化的暴力是把子树的最优解放进set里,然后直接跳father:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const int inf = 0x3f3f3f3f; int n, q, fa[maxn], ans; bool vis[maxn]; multiset<int> s[maxn]; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }a[maxn<<1]; int head[maxn], len; void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } void dfs(int x, int f) { fa[x] = f; for(int i=head[x]; i; i=a[i].next) { int y = a[i].to; if(y == fa[x]) continue; dfs(y, x); } } void update0(int x) { int tp = 0; while(x) { s[x].erase(s[x].find(tp)); tp++; x = fa[x]; } } void update1(int x) { int tp = 0; while(x) { s[x].insert(tp); tp++; x = fa[x]; } } void query(int x) { int tp = 0; ans = inf; while(x) { if(s[x].size()) ans = min(ans, (*s[x].begin())+tp); x = fa[x]; tp++; } if(ans == inf) ans = -1; } int main() { n = read(); q = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } dfs(1, 0); while(q--) { int opt = read(); if(opt == 1) { int x = read(); if(vis[x] == 1) {vis[x] = 0; update0(x);} else {vis[x] = 1; update1(x);} } else { int x = read(); query(x); printf("%d\n", ans); } } return 0; }
直接bfs:(本来第一个想到bfs然后以为它太慢。。笑死了开什么优化)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const int inf = 0x3f3f3f3f; int n, q, sum; bool col[maxn], vis[maxn]; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }a[maxn<<1]; int head[maxn], len; void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } int bfs(int stx) { int cnt = 0; queue<pair<int, int> > q; memset(vis, 0, sizeof(vis)); q.push(make_pair(stx, cnt)); while(!q.empty()) { pair<int, int> x = q.front(); q.pop(); if(col[x.first] == 1) return x.second; vis[x.first] = true; for(int i=head[x.first]; i; i=a[i].next) { int to = a[i].to; if(!vis[to]) { if(col[to]) return x.second+1; q.push(make_pair(to, x.second+1)); } } } return -1;//useless but because of warning } int main() { n = read(); q = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } while(q--) { int opt = read(); if(opt == 1) { int x = read(); if(!col[x]) sum++; else sum--; col[x] ^= 1; } else { int x = read(); if(!sum) printf("-1\n"); else printf("%d\n", bfs(x)); } } return 0; }
问题 C: 【2022NOIP联测710月11日】朋(c)
每一个左边的点都能找到匹配,所以对左边的点的匹配生成排列,判断一下and和是谁:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 109; const int inf = 0x3f3f3f3f; int n, m, ext[133], num; bool vis[maxn]; vector<int> v[maxn], w[maxn]; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } void dfs(int a, int n) { if(a > n) { if(num <= 127) ext[num] = 1; return; } int sz = v[a].size(); for(int i=0; i<sz; i++) { int to = v[a][i]; if(vis[to]) continue; vis[to] = 1; int now = num; if(a == 1) num = w[a][i]; else num &= w[a][i]; dfs(a+1, n); vis[to] = 0; num = now; } } int main() { n = read(); m = read(); for(int i=1; i<=m; i++) { int x = read(), y = read(), z = read(); v[x].push_back(y); w[x].push_back(z); } dfs(1, n); for(int i=0; i<=127; i++) { printf("%d", ext[i]); } return 0; }
当初学网络流的初心就是让它在这种时候派上用场,然而我并不会活学活用(样例都过不了但是可以A就诡异),鹤的,from gtm 1514:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 109; const int inf = 0x3f3f3f3f; int n, m, s, t, len=1, head[2010], head2[2010], dis[2010]; 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int v, w, val, next, weight; }a[2000010]; void add(int u, int v, int w, int weight) { a[++len].v = v; a[len].next = head[u]; head[u] = len; a[len].val = w; a[len].w = w; a[len].weight = weight; } queue<int> q; bool bfs(int st, int k) { for(int i=0; i<=2*n+1; i++) head2[i] = head[i], dis[i] = 0; q.push(st); dis[st] = 1; while(!q.empty()) { int x = q.front(); q.pop(); for(int i=head[x]; i; i=a[i].next) { if((k&a[i].weight)==k&&!dis[a[i].v]&&a[i].w) { dis[a[i].v] = dis[x]+1; if(a[i].v == t) { while(!q.empty()) q.pop(); return true; } q.push(a[i].v); } } } return false; } int dfs(int x, int flow, int k) { if(x == t) return flow; int sum = 0;; for(int &i=head2[x]; i; i=a[i].next) { if((k&a[i].weight)==k&&dis[a[i].v]==dis[x]+1&&a[i].w) { int ret = dfs(a[i].v, min(flow, a[i].w), k); if(ret) { a[i].w -= ret; a[i^1].w += ret; sum += ret; flow -= ret; if(!flow) return sum; } else dis[a[i].v] = -1; } } return sum; } int dinic(int k) { int ans = 0; while(bfs(s, k)) ans += dfs(s, 2147483647, k); return ans; } int main() { n = read(); m = read(); t = 2*n+1; for(int i=1; i<=m; i++) { int u = read(), v = read(), w = read(); add(u, v+n, 1, w); add(v+n, u, 0, w); } for(int i=1; i<=n; i++) { add(s, i, 1, 127); add(i, s, 0, 127); add(i+n, t, 1, 127); add(t, i+n, 0, 127); } for(int i=0; i<128; i++) { for(int j=2; j<=len; j++) a[j].w = a[j].val; int ans = dinic(i); if(ans == n) printf("1"); else printf("0"); } return 0; }
问题 D: 【2022NOIP联测710月11日】友(d)
对每一个点维护以它为根的子树的合法决策放进set,合法的条件是a递减b也递减,这里的a,b是求和之后的也就是suma,sumb,子树向上合并时先满足b递减的条件,如果前驱(b更大)而a更小不满足单调性,当前决策不优,删掉,如果后继(b更小)而a更大那么这个后继不优,删掉,继续找下一个后继直到找到一个不用被删了的为止。
最后b最大的s[x].begin()就是以x为根的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1003; const int inf = 0x3f3f3f3f; int n, m, a[maxn], b[maxn]; ll 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }e[maxn<<1]; int head[maxn], len; void add(int x, int y) { e[++len].to = y; e[len].next = head[x]; head[x] = len; } struct cho { ll a, b; bool operator < (const cho &T) const { return b > T.b; } }; vector<cho> ve; set<cho> s[maxn], sp; set<cho>::iterator it; void dfs(int x, int fa) { s[x].insert((cho){a[x], b[x]}); for(int i=head[x]; i; i=e[i].next) { int y = e[i].to; if(y == fa) continue; dfs(y, x); sp.clear(); for(auto p : s[x]) { sp.insert(p); } for(auto p : sp) { for(auto q : s[y]) { if(p.a + q.a > m) continue; cho r = (cho){p.a+q.a, p.b+q.b}; s[x].insert(r); it = s[x].lower_bound(r); if(it != s[x].begin()) { auto dr = it; dr--; if((*it).a > (*dr).a) s[x].erase(it); else { dr++; dr++; for(; dr!=s[x].end(); dr++) { if((*it).a < (*dr).a) ve.push_back(*dr); else break; } if(ve.size()) { for(auto f : ve) s[x].erase(f); ve.clear(); } } } else { auto dr = it; dr++; for(; dr!=s[x].end(); dr++) { if((*it).a < (*dr).a) ve.push_back(*dr); else break; } if(ve.size()) { for(auto f : ve) s[x].erase(f); ve.clear(); } } } } } it = s[x].begin(); ans = max(ans, (*it).b); } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { a[i] = read(); b[i] = read(); } for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } dfs(1, 0); printf("%lld\n", ans); return 0; }
好像又觉得套个点分治,也没什么好说的……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1003; const int N = 1e4 + 6; const int inf = 0x3f3f3f3f; int n, m, a[maxn], b[maxn], siz[maxn], mx[maxn], sum, root; int dfn[maxn], dfnr[maxn], id[maxn], tim; bool vis[maxn]; ll f[maxn][N], 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 << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int next, to; }e[N]; int head[maxn], len; void add(int x, int y) { e[++len].to = y; e[len].next = head[x]; head[x] = len; } void findroot(int x, int fa) { siz[x] = 1; mx[x] = 0; for(int i=head[x]; i; i=e[i].next) { int v = e[i].to; if(v == fa || vis[v]) continue; findroot(v, x); siz[x] += siz[v]; mx[x] = max(mx[x], siz[v]); } mx[x] = max(mx[x], sum-siz[x]); if(mx[x] < mx[root]) root = x; } void dfs(int x, int fa) { dfn[x] = ++tim; id[tim] = x; for(int i=head[x]; i; i=e[i].next) { int v = e[i].to; if(v == fa || vis[v]) continue; dfs(v, x); } dfnr[dfn[x]] = tim; } void calc(int x) { tim = 0; dfs(x, 0); for(int i=1; i<=tim+1; i++) { for(int j=0; j<=m; j++) f[i][j] = -inf; } f[1][0] = 0; for(int i=1; i<=tim; i++) { if(i + 1 <= dfnr[i]) { int now = id[i]; for(int j=0; j<=m-a[now]; j++) { f[i+1][j+a[now]] = max(f[i+1][j+a[now]], f[i][j]+b[now]); } } for(int j=0; j<=m; j++) { int now = id[i]; if(j + a[now] <= m) f[dfnr[i]+1][j+a[now]] = max(f[dfnr[i]+1][j+a[now]], f[i][j]+b[now]); f[dfnr[i]+1][j] = max(f[dfnr[i]+1][j], f[i][j]); } } for(int i=0; i<=m; i++) ans = max(ans, f[tim+1][i]); } void solve(int x) { calc(x); vis[x] = true; for(int i=head[x]; i; i=e[i].next) { int v = e[i].to; if(vis[v]) continue; sum = siz[v]; root = 0; findroot(v, 0); solve(root); } } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) a[i] = read(), b[i] = read(); for(int i=1; i<n; i++) { int u = read(), v = read(); add(u, v); add(v, u); } mx[0] = sum = n; findroot(1, 0); solve(root); printf("%lld\n", ans); return 0; }
时光花火,水月星辰