CSP-S模拟18
A. 最长反链
我居然以为它不连续的也不行,我怀疑这个20分的东西把题面改成“x不是y的子序列”大概是对的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1337; int T, l, r, h1, t1, Max, h2; 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 qpow(int a, int b) { int ans = 1; while(b) { if(b & 1) ans = ans * a; a = a * a; b >>= 1; } return ans; } int main() { T = read(); while(T--) { l = read(); r = read(); int ll = l, rr = r; h1 = 0, t1 = 0; while(rr) { h1++; t1 = rr % 10; rr /= 10; } Max = qpow(10, h1-1); h2 = 0; while(ll) { h2++; ll /= 10; } if(h1 == h2) { printf("%d\n", r-l+1); continue; } if(t1 != 1) { printf("%d\n", r-Max+1); continue; } int base = r % Max; int m2 = qpow(10, h1-2); if(base < m2) base += m2; if(base < l) base = l - 1; printf("%d\n", r-base); } return 0; }
然而我赛时连这20都没有,因为我算出来的l没有和题目里给的l取min。。。
于是改成连续的就可以了:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1337; int T, l, r, h1, t1, Max, h2; 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 qpow(int a, int b) { int ans = 1; while(b) { if(b & 1) ans = ans * a; a = a * a; b >>= 1; } return ans; } int main() { T = read(); while(T--) { l = read(); r = read(); int ll = l, rr = r; h1 = 0, t1 = 0; while(rr) { h1++; t1 = rr % 10; rr /= 10; } Max = qpow(10, h1-1); h2 = 0; while(ll) { h2++; ll /= 10; } if(h1 == h2) { printf("%d\n", r-l+1); continue; } if(t1 != 1) { printf("%d\n", r-Max+1); continue; } int base = r % Max; int m2 = r / 10; if(base < m2) base = m2; if(base < l) base = l - 1; printf("%d\n", r-base); } return 0; }
B. 2A+x
WA 51:先分两种大的情况,一种是所有点都需要操作,一种是只有一部分点需要操作。所有点操作一遍的最好情况就是差值变成max(2*原始差值-x, 0),也就是2*原始差值-x<原始差值,原始差值<x。这种情况无限操作就可以无限减小差值,差值总会变成0。
然后对于剩下的,a最大的那个一定不会被操作,我保存了每个数尽量靠近它的两个值(小于等于的最大mi和大于等于的最小mx),操作的结果被我分成3种情况,原来的最大a记为Max,全取mi,Max依然最大,全取mx,Max成为最小,还有都尽量取绝对值小的,Max什么都不是。
错因可能是第3种情况我对于操作后不满足Max什么都不是的直接弃掉了,但是其实可能会出更优答案。是不是这么错的其实我也不很确定……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const ll inf = 1e13; int n, v, a[maxn]; ll d[maxn], mi[maxn], mx[maxn], Max, 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; } ll qpow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a; a = a * a; b >>= 1; } return ans; } int main() { n = read(); v = read(); for(int i=1; i<=n; i++) a[i] = read(); sort(a+1, a+1+n); if(a[n] - a[1] <= v) { printf("0\n"); exit(0); } Max = a[n]; for(int i=1; i<n; i++) { mi[i] = a[i]; mx[i] = inf; for(int j=1; true; j++) { ll f = qpow(2, j); if(a[i]*f+v*(f-1) >= Max) { mx[i] = min(mx[i], max(a[i]*f,Max)); } if(a[i]*f <= Max) { mi[i] = max(mi[i], min(a[i]*f+v*(f-1),Max)); } else break; } } memcpy(d, mx, sizeof(mx)); sort(d+1, d+n); ans = d[n-1] - Max; memcpy(d, mi, sizeof(mi)); sort(d+1, d+n); ans = min(ans, Max - d[1]); for(int i=1; i<n; i++) { if(Max - mi[i] <= mx[i] - Max) d[i] = mi[i]; else d[i] = mx[i]; } sort(d+1, d+n); if(d[1] < Max && d[n-1] > Max) ans = min(ans, d[n-1] - d[1]); printf("%lld\n", ans); return 0; }
然后我鹤了sandom的题解:首先我发现了找到步数不需要循环,可以用log2函数,还有可以先缩小一下差距再和x比较,全取mi让Max依然最大是一样的,接下来把修改后的数组仍然按a排序讨论取mx的情况。因为a已经排过序了,如果选到了一个值让它mi->mx,那它左边的都可以这样做,因为mi->mx其实就是左端点*2,比它更小的*2之后一定依然小于它,当前的最小值就是没被操作的那个数的mi。
这个过程没有更改a的值,所以我把最后一个排序删掉了。并且根据最后一个数不需要被操作的原理,我把所有的n从循环里去掉了,但是n的b值(“修改”后的mi也就是小于等于的最大值)需要赋一下初值就是它自己,我的魔改应该是对的实测可以过。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const ll inf = 1e13; int n; ll x, ans = 1e18, bow[60]; 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 { ll a, k, b; bool operator < (const node T) const { return a < T.a; } }d[maxn], e[maxn]; int main() { bow[0] = 1; for(int i=1; i<=50; i++) bow[i] = bow[i-1] * 2; n = read(); x = read(); for(int i=1; i<=n; i++) d[i].a = read(); sort(d+1, d+1+n); for(int i=1; i<n; i++) { //1.这个区间可以一步确定!!2^k可以预处理 d[i].k = log2(d[n].a/d[i].a);//下限可以直接算,k是步数 d[i].a = bow[d[i].k]*d[i].a;//直接扩大 if(d[i].a+(bow[d[i].k]-1)*x>=d[n].a) d[i].a = d[n].a, d[i].k = 0;//如果可以相等 d[i].b = d[i].a + (bow[d[i].k]-1)*x;//否则取的是小于等于的最大值 //不需要取大于等于的最小值吗?? //不需要分类讨论吗?? } d[n].b = d[n].a; sort(d+1, d+1+n); ll gap = 1e18, pos = 0; for(int i=1; i<n; i++) gap = min(gap, d[i].b); //2.全体乘2不一定在一开始,还可以是先缩小差距 //这个我大概是错了 if(d[n].a - gap < x) { printf("0\n"); exit(0); } ans = min(ans, d[n].a-gap); //都不乘2还是不都乘2? gap = d[n].b;//让最后一个数当一次最小值 for(int i=n-1; i>=1; i--) { ans = min(ans, 2*d[i].a-gap);//分类讨论在这里!!让每个数尝试取一下较大值 //然而最大和最小怎么可能同时让一个数取?这里指n gap = min(gap, d[i].b); } //sort(d+1, d+1+n); //printf("%lld\n", min(ans, d[n].a-d[1].a)); printf("%lld\n", ans); return 0; }
C. No Rest for the Wicked
我写了一个暴力是按美丽度从大到小从终点到起点的那种,更新方式是bfs,然而0分了(几乎全T还有错的),目前还很迷惑……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 6; const int inf = 1e9 + 7; int n, m, c[maxn], t[maxn], s[maxn], val[maxn], cnt, mi[maxn]; bool vis[maxn]; queue<pair<int, int> > q; 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 node1 { int id, c, t, s; bool operator < (const node1 &T) const { return s > T.s; } }p[maxn]; 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 st) { int ans = 1; val[p[st].id] = p[st].s; q.push(make_pair(p[st].t, p[st].id)); while(!q.empty()) { int lim = q.front().first, x = q.front().second; q.pop(); vis[x] = 1; if(val[x] < p[st].s) { val[x] = p[st].s; ans++; } for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(c[v] <= lim && (!vis[v] || min(lim, t[v]) > mi[i])) { mi[i] = min(lim, t[v]); q.push(make_pair(mi[i], v)); } } } return ans; } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { p[i].id = i; p[i].c = read(); p[i].t = read(); p[i].s = read(); c[i] = p[i].c; t[i] = p[i].t; s[i] = p[i].s; } sort(p+1, p+1+n); cnt = n; for(int i=1; i<=m; i++) { int x = read(), y = read(); if(c[x] <= t[y]) add(x, y); if(c[y] <= t[x]) add(y, x); } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) vis[j] = 0; for(int j=1; j<=n; j++) mi[i] = t[i]; int del = bfs(i); cnt -= del; if(!cnt) break; } for(int i=1; i<=n; i++) printf("%d ", val[i]); return 0; }
于是我就去鹤了一下有分版的暴力,发现我的写法几乎没有什么思考,没有记录答案因为没有想到“考虑f(x, v)表示从疫情值为x的点v出发能够得到的最大美丽度,那么后面再扫到v并且新的疫情值小于等于x时就没有再搜下去的必要了,直接取f(x, v)即可”,我让它一直进去搜,搜了一遍再搜一遍,不T才怪……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//鹤: #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 6; const int inf = 1e9 + 7; int n, m, ans[maxn], st, minnt[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; }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 city { int s, t, c; }a[maxn]; struct my { int s, id; bool operator < (const my &T) const { return s > T.s; } }b[maxn]; void dfs(int x, int mint) { ans[x] = max(a[st].s, ans[x]); minnt[x] = max(minnt[x], mint); for(int i=head[x]; i; i=e[i].next) { int v = e[i].to; //要么可以更新答案,要么可以放宽上界 if((ans[v]<a[st].s||minnt[v]<mint) && a[v].c<=mint) { dfs(v, min(mint, a[v].t)); } } } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { a[i].c = read(); a[i].t = read(); a[i].s = read(); b[i].s = a[i].s; b[i].id = i; } sort(b+1, b+1+n); for(int i=1; i<=m; i++) { int x = read(), y = read(); add(x, y); add(y, x); } for(int i=1; i<=n; i++) { st = b[i].id; dfs(st, a[st].t); } for(int i=1; i<=n; i++) { printf("%d ", ans[i]); } return 0; }
正解是线段树分治(动态图连通性),我又想直接粘图了,from Chen_jr:
关于单向边的那个式子,我觉得意思是不一定能从j走回i,好像只判断了单向走可行但是并没有说反向不行,所以双向边也包含了单向边,不过建重复了无所谓反正时间是一样的,同时加入同时删除对连通性不造成影响。
一般的线段树分治节点都代表时间,因为一个边是否存在都和时间有关,但是在这道题里边的存在和疫情的标准有关(下界?为什么我觉得应该叫它上界。。难道它指的不是经过国家的最大疫情值吗?不过这个下界也可能指的是最劣情况的意思)。不过疫情值并不是真的时间,搜索顺序可以任意,所以可以和暴力搜索一样按照疫情值从大到小考虑作为优化。
关于代码:我上网搜了一下才想起来了int &的用法,https://blog.csdn.net/willian0621/article/details/12838157
所以swap(u, v)不仅交换了u和v还交换了e[i].u和e[i].v。还有加入双向边的时候我想还是应该判断一下的,不过在这里不判断也没有关系因为把L < R传进去肯定没有符合要求的区间,所以不造成任何修改,和“三级跳”那个题不一样的是那个题有pushup,进不去的话会导致叶子被它不存在的孩子更新为空。
我发现我鹤的代码里的merge函数和某些运算符重载始终没有用到,于是就把它们删掉了,关于国家的结构体莫名其妙的变成了city这件事……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 4e5 + 5; const int inf = 1e9 + 7; int n, m, ls[maxn+maxn], f[maxn], siz[maxn], val[maxn], ans[maxn]; bool vis[maxn]; vector<int> sol[maxn+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 fa(int x) {return f[x] == x ? x : fa(f[x]);} struct edge { int u, v; }e[maxn]; struct city//为什么国家变成了城市。。 { int c, t, s, id; }d[maxn]; struct sta { bool dir; int fx, x, valfx, tim; }st[maxn]; int top; void add_edge(int tim, int id, bool dir) { if(dir) { int u = e[id].u, v = e[id].v; u = fa(u), v = fa(v); if(val[u] < ans[v]) { ++top; st[top].tim = tim; st[top].dir = dir; st[top].x = st[top].fx = u; st[top].valfx = val[u]; val[u] = ans[v]; } } else { int u = e[id].u, v = e[id].v; u = fa(u), v = fa(v); if(u == v) return; if(siz[u] < siz[v]) swap(u, v); ++top; st[top].x = v; st[top].fx = u; st[top].valfx = val[u]; st[top].dir = dir; st[top].tim = tim; f[v] = u; val[u] = max(val[u], val[v]); siz[u] += siz[v]; } } void del(int tim) { while(top && st[top].tim == tim) { if(st[top].dir) { val[st[top].x] = st[top].valfx; } else { int u = st[top].fx, v = st[top].x; siz[u] -= siz[v]; f[v] = v; val[u] = st[top].valfx; } --top; } } struct seg { struct node { vector<int> dir, nir; }t[maxn<<2]; void insert(int x, int l, int r, int L, int R, int id, bool dir) { if(L <= l && r <= R) { if(dir) t[x].dir.push_back(id); else t[x].nir.push_back(id); return; } int mid = (l + r) >> 1; if(L <= mid) insert(x<<1, l, mid, L, R, id, dir); if(R > mid) insert(x<<1|1, mid+1, r, L, R, id, dir); } void solve(int x, int l, int r) { for(int v : t[x].dir) add_edge(x, v, 1); for(int v : t[x].nir) add_edge(x, v, 0); if(l == r) { for(int v : sol[l]) ans[v] = max(ans[v], val[fa(v)]); del(x); return; } int mid = (l + r) >> 1; solve(x<<1|1, mid+1, r); solve(x<<1, l, mid); del(x); } }t; int main() { n = read(); m = read(); for(int i=1; i<=n; i++) d[i].c = read(), d[i].t = read(), d[i].s = read(); for(int i=1; i<=m; i++) e[i].u = read(), e[i].v = read(); int cnt = 0; for(int i=1; i<=n; i++) ls[++cnt] = d[i].c, ls[++cnt] = d[i].t; sort(ls+1, ls+cnt+1); cnt = unique(ls+1, ls+cnt+1)-ls-1; for(int i=1; i<=n; i++) d[i].c = lower_bound(ls+1, ls+cnt+1, d[i].c)-ls; for(int i=1; i<=n; i++) d[i].t = lower_bound(ls+1, ls+cnt+1, d[i].t)-ls; for(int i=1; i<=n; i++) sol[d[i].c].push_back(i); for(int i=1; i<=m; i++) { int &u = e[i].u, &v = e[i].v; if(d[u].c > d[v].c) swap(u, v); if(d[v].c <= min(d[v].t, d[u].t)) t.insert(1, 1, cnt, d[v].c, min(d[v].t, d[u].t), i, 0); if(d[u].c <= min(d[v].c-1, d[u].t)) t.insert(1, 1, cnt, d[u].c, min(d[v].c-1, d[u].t), i, 1); } for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1, val[i] = d[i].s, ans[i] = d[i].s; t.solve(1, 1, cnt); for(int i=1; i<=n; i++) printf("%d ", ans[i]); return 0; }
D. 暴力题
4分?!我以为应该有5分的……难道是因为没用逆元??
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 6; const ll mod = 998244353; int n, k, w, a[maxn], q; ll ans; multiset<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; } ll qpow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } int main() { n = read(); k = read(); w = read(); for(int i=1; i<=n; i++) { a[i] = read(); s.insert(a[i]); } q = read(); while(q--) { int pos = read(), x = read(); s.erase(s.find(a[pos])); a[pos] = x; s.insert(a[pos]); int i = 1; ans = 0; for(int y : s) { ll ad = (double)y/w*qpow(i, k); ad %= mod; ans = (ans + ad)%mod; i++; } printf("%lld\n", ans); } return 0; }