除了I题以外的题的题解
A.鼠鼠打洞 https://ac.nowcoder.com/acm/contest/71344/A
观察题目条件后用哈希表记录洞出现的次数,累加到答案。
void solve() { string s; cin >> s; map<char, int> m; m['A'] = m['D'] = m['O'] = m['P'] = m['Q'] = m['R'] = 1; m['B'] = 2; m['a'] = m['b'] = m['d'] = m['e'] = m['g'] = m['o'] = m['p'] = m['q'] = 1; m['0'] = m['4'] = m['6'] = m['9'] = 1; m['8'] = 2; int ans = 0; for (char c : s) ans += m[c]; cout << ans << endl; }
B.scx,做规划 https://ac.nowcoder.com/acm/contest/71344/B
容易发现题目数据保证 1 <= L <= R <= 60,也就是说可以对每个 L <= D <= R,暴力统计任务为第 D 天且出现在以 x 节点为根节点的子树内的节点数量,这里使用DFS序与区间查询总和单点修改的数据结构维护。
对于 L, R 没啥限制的情况,可以使用比较恶心人的树套树来维护。
struct HLD { int n; std::vector<int> siz, top, dep, parent, in, out, seq; std::vector<std::vector<int>> adj; int cur; HLD() {} HLD(int n) { init(n); } void init(int n) { this->n = n; siz.resize(n); top.resize(n); dep.resize(n); parent.resize(n); in.resize(n); out.resize(n); seq.resize(n); cur = 0; adj.assign(n, {}); } void addEdge(int u, int v) { adj[u].push_back(v); adj[v].push_back(u); } void work(int root = 0) { top[root] = root; dep[root] = 0; parent[root] = -1; dfs1(root); dfs2(root); } void dfs1(int u) { if (parent[u] != -1) { adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u])); } siz[u] = 1; for (auto &v : adj[u]) { parent[v] = u; dep[v] = dep[u] + 1; dfs1(v); siz[u] += siz[v]; if (siz[v] > siz[adj[u][0]]) { std::swap(v, adj[u][0]); } } } void dfs2(int u) { in[u] = cur++; seq[in[u]] = u; for (auto v : adj[u]) { top[v] = v == adj[u][0] ? top[u] : v; dfs2(v); } out[u] = cur; } int lca(int u, int v) { while (top[u] != top[v]) { if (dep[top[u]] > dep[top[v]]) { u = parent[top[u]]; } else { v = parent[top[v]]; } } return dep[u] < dep[v] ? u : v; } int dist(int u, int v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; } int jump(int u, int k) { if (dep[u] < k) { return -1; } int d = dep[u] - k; while (dep[top[u]] > d) { u = parent[top[u]]; } return seq[in[u] - dep[u] + d]; } bool isAncester(int u, int v) { return in[u] <= in[v] && in[v] < out[u]; } int rootedParent(int u, int v) { std::swap(u, v); if (u == v) { return u; } if (!isAncester(u, v)) { return parent[u]; } auto it = std::upper_bound(adj[u].begin(), adj[u].end(), v, [&](int x, int y) { return in[x] < in[y]; }) - 1; return *it; } int rootedSize(int u, int v) { if (u == v) { return n; } if (!isAncester(v, u)) { return siz[v]; } return n - siz[rootedParent(u, v)]; } int rootedLca(int a, int b, int c) { return lca(a, b) ^ lca(b, c) ^ lca(c, a); } }; void solve() { int n, m; cin >> n >> m; vector<int> a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; HLD hld(n + 1); for (int i = 1; i <= n - 1; i++) { int u, v; cin >> u >> v; hld.addEdge(u, v); } hld.work(1); vector<vector<int>> bit(61, vector<int>(n + 1)); auto update = [&](vector<int> &b, int x, int v) -> void { x++; while (x <= n) { b[x] += v; x += lowbit(x); } }; auto query = [&](vector<int> &b, int x) -> int { x++; int r = 0; while (x) { r += b[x]; x -= lowbit(x); } return r; }; for (int i = 1; i <= n; i++) { update(bit[a[i]], hld.in[i], 1); } for (int i = 1; i <= m; i++) { int op; cin >> op; if (op == 1) { int x, y; cin >> x >> y; update(bit[a[x]], hld.in[x], -1); a[x] = y; update(bit[a[x]], hld.in[x], 1); } else { int x, l, r; cin >> x >> l >> r; int ans = 0; for (int i = l; i <= r; i++) { ans += query(bit[i], hld.out[x] - 1) - query(bit[i], hld.in[x] - 1); } cout << ans << endl; } } }
C.广告位招商中 https://ac.nowcoder.com/acm/contest/71344/C
对获得的广告费累加求和之后判定是否大于等于 M + 50
void solve() { int n, m; cin >> n >> m; vector<int> a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; ll s = 0; for (int i = 1; i <= n; i++) s += a[i]; if (s >= m + 50) { cout << "KFC" << endl; } else { cout << "QAQ" << endl; } }
D.最惨烈的代价 https://ac.nowcoder.com/acm/contest/71344/D
对下标进行稳定的排序(对于相等的元素保持它们的相对顺序),然后使用前 (N + 1) / 2 个,有点卡常,sort好像过不去,得手写排序。
namespace Fast_IO{ const int MAXL((1 << 18) + 1);int iof, iotp; char ioif[MAXL], *ioiS, *ioiT, ioof[MAXL],*iooS=ioof,*iooT=ioof+MAXL-1,ioc,iost[55]; char Getchar(){ if (ioiS == ioiT){ ioiS=ioif;ioiT=ioiS+fread(ioif,1,MAXL,stdin);return (ioiS == ioiT ? EOF : *ioiS++); }else return (*ioiS++); } void Write(){fwrite(ioof,1,iooS-ioof,stdout);iooS=ioof;} void Putchar(char x){*iooS++ = x;if (iooS == iooT)Write();} inline int read(){ int x=0;for(iof=1,ioc=Getchar();(ioc<'0'||ioc>'9')&&ioc!=EOF;)iof=ioc=='-'?-1:1,ioc=Getchar(); if(ioc==EOF)Write(),exit(0); for(x=0;ioc<='9'&&ioc>='0';ioc=Getchar())x=(x<<3)+(x<<1)+(ioc^48);return x*iof; } inline long long read_ll(){ long long x=0;for(iof=1,ioc=Getchar();(ioc<'0'||ioc>'9')&&ioc!=EOF;)iof=ioc=='-'?-1:1,ioc=Getchar(); if(ioc==EOF)Write(),exit(0); for(x=0;ioc<='9'&&ioc>='0';ioc=Getchar())x=(x<<3)+(x<<1)+(ioc^48);return x*iof; } void Getstr(char *s, int &l){ for(ioc=Getchar();ioc==' '||ioc=='\n'||ioc=='\t';)ioc=Getchar(); if(ioc==EOF)Write(),exit(0); for(l=0;!(ioc==' '||ioc=='\n'||ioc=='\t'||ioc==EOF);ioc=Getchar())s[l++]=ioc;s[l] = 0; } template <class Int>void Print(Int x, char ch = '\0'){ if(!x)Putchar('0');if(x<0)Putchar('-'),x=-x;while(x)iost[++iotp]=x%10+'0',x/=10; while(iotp)Putchar(iost[iotp--]);if (ch)Putchar(ch); } void Putstr(const char *s){for(int i=0,n=strlen(s);i<n;++i)Putchar(s[i]);} } // namespace Fast_IO using namespace Fast_IO; int a[int(5e6 + 1)], p[int(5e6 + 1)], t[int(5e6 + 1)]; void mergesort(int l, int r) { if (l == r) return; int mid = l + (r - l) / 2; mergesort(l, mid); mergesort(mid + 1, r); int i = l, j = mid + 1, pos = l; while (i <= mid && j <= r) { if (a[p[i]] <= a[p[j]]) { t[pos++] = p[i++]; } else { t[pos++] = p[j++]; } } while (i <= mid) { t[pos++] = p[i++]; } while (j <= r) { t[pos++] = p[j++]; } for (int i = l; i <= r; i++) { p[i] = t[i]; } } void solve() { int n; n = read(); for (int i = 1; i <= n; i++) { a[i] = read(); p[i] = i; } mergesort(1, n); ll ans = 0; for (int i = 1; i <= (n + 1) / 2; i++) { ans += a[p[i]]; a[p[i]] = -1; } Print(ans, '\n'); for (int i = 1; i <= n; i++) { if (a[i] == -1) Print(i, ' '); } Write(); }
E.递交申请 https://ac.nowcoder.com/acm/contest/71344/E
对每个点进行记忆化搜索,注意判定有环的情况。
void solve() { int n, q; cin >> n >> q; vector<int> x(n + 1), t(n + 1); for (int i = 1; i <= n; i++) { cin >> x[i] >> t[i]; } vector<ll> dis(n + 1, -1); dis[0] = 0; function<ll(int)> dfs = [&](int u) -> ll { if (dis[u] != -1) return dis[u]; dis[u] = inf; //方便判环,环内节点距离设置成极大值 dis[u] = dfs(x[u]) + t[u]; dis[u] = min(dis[u], inf); return dis[u]; }; for (int i = 1; i <= q; i++) { int d, x; cin >> d >> x; if (dfs(x) <= d) { cout << "Holiday" << endl; } else { cout << "Stay" << endl; } } }
F.信念中心 https://ac.nowcoder.com/acm/contest/71344/F
真正意义上的缝合题。
求最小圆覆盖后除一下凸包面积。
struct vec { double x, y; vec (const double& x0 = 0, const double& y0 = 0) : x(x0), y(y0) {} vec operator + (const vec& t) const {return vec(x+t.x, y+t.y);} vec operator - (const vec& t) const {return vec(x-t.x, y-t.y);} vec operator * (const double& t) const {return vec(x*t, y*t);} vec operator / (const double& t) const {return vec(x/t, y/t);} const double len2 () const {return x*x + y*y;} const double len () const {return sqrt(len2());} vec norm() const {return *this/len();} vec rotate_90_c () {return vec(y, -x);} }; double dot(const vec& a, const vec& b) {return a.x*b.x + a.y*b.y;} double crs(const vec& a, const vec& b) {return a.x*b.y - a.y*b.x;} vec lin_lin_int(const vec& p0, const vec& v0, const vec& p1, const vec& v1) { double t = crs(p1-p0, v1) / crs(v0, v1); return p0 + v0 * t; } vec circle(const vec& a, const vec& b, const vec& c) { return lin_lin_int((a+b)/2, (b-a).rotate_90_c(), (a+c)/2, (c-a).rotate_90_c()); } int n; vec pot[500005]; #define unllong unsigned long long #define unint unsigned int #define printline printf( "\n" ) typedef long long llong ; //const double PI = 2.0 * acos( 0.0 ) ; #define zero(x) (((x)>0?(x):-(x))<eps) const int Base=1000000000;//高精度 const int Capacity=1000;//高精度 const double eps = 1e-16 ; const int INF = 1000000 ; const int siz = 500100 ; struct POINT { double x ; double y ; double k ; }; struct POINT point[siz] ; int stak[siz] ; int top = 2 ; int inn ; double outarea ; double fdist( double x1, double y1, double x2, double y2 ) { return sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ) ; } void input() { int leftdown = 0 ; for( int i=0; i<inn; i++ ) { //if( miny>point[i].y || miny==point[i].y&&minx>point[i].x ) if( point[leftdown].y>point[i].y||zero(point[leftdown].y-point[i].y)&&point[leftdown].x>point[i].x ) leftdown = i ;//找到最左下的点 } double temp ; temp = point[0].x ; point[0].x = point[leftdown].x ; point[leftdown].x = temp ; temp = point[0].y ; point[0].y = point[leftdown].y ; point[leftdown].y = temp ; for( int i=1; i<inn; i++ ) { point[i].k = atan2( point[i].y-point[0].y, point[i].x-point[0].x ) ; }//以点(minx, miny)计算极角 } double xmult( POINT &p1, POINT &p2, POINT &p0 ) {//计算叉乘--线段旋转方向和对应的四边形的面积--返回(p1-p0)*(p2-p0)叉积 //if叉积为正--p0p1在p0p2的顺时针方向; if(x==0)共线 return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y) ; } int gramcmp1( const void *a, const void *b ) { struct POINT *c = (struct POINT *)a ; struct POINT *d = (struct POINT *)b ; if( c->k - d->k > eps ) return 1 ; else if( c->k - d->k < -1*eps ) return -1 ; else//斜率相等距离远的点在先 return c->x - d->x > 0 ? 1 : -1 ; } int gramcmp( const void *a, const void *b ) { struct POINT *c = (struct POINT *)a ; struct POINT *d = (struct POINT *)b ; double xmult_val = xmult( *c, *d, point[0] ) ; if( xmult_val > eps ) return -1 ; else if( xmult_val < -1*eps ) return 1 ; else return c->x - d->x > 0 ? 1 : -1 ; //else //return fdist( c->x,c->y,point[0].x,point[0].y )>fdist(d->x,d->y,point[0].x,point[0].y)? -1:1 ; } void gramham() {//凸包的点存在于stack[]中 qsort( point+1, inn-1, sizeof(point[1]), gramcmp ) ;//极坐标排序--注意只有(n-1)个点 //int stack[size] ; int top = 2 ; stak[0] = 0 ; stak[1] = 1 ; stak[2] = 2 ; top = 2 ; for( int i=3; i<inn; i++ ) { while( top>=1&&xmult( point[i], point[stak[top]], point[stak[top-1]] )>=-1*eps ) top-- ;//顺时针方向--删除栈顶元素 stak[++top] = i ;//新元素入栈 } /* for( int i=0; i<=top; i++ ) { //printf( "%lf===%lf\n",point[stack[i]].x, point[stack[i]].y ) ; cout << point[stack[i]].x << "====" << point[stack[i]].y << endl ; } */ } double flen_poly() {//计算凸包的周长 double len = 0.0 ; double x1, x2, y1, y2 ; for( int i=0; i<top; i++ ) { x1 = point[stak[i+1]].x ; x2 = point[stak[i]].x ; y1 = point[stak[i+1]].y ; y2 = point[stak[i]].y ; len += fdist( x1, y1, x2, y2 ) ; } x1 = point[stak[0]].x ; x2 = point[stak[top]].x ; y1 = point[stak[0]].y ; y2 = point[stak[top]].y ; len += fdist( x1, y1, x2, y2 ) ; return len ; } double farea_poly( int n, POINT poly[] ) { double area = 0.0 ; double s1 = 0.0 , s2 = 0.0 ; for( int i=0; i<n; i++ ) { s1 += poly[stak[(i+1)%n]].y * poly[stak[i%n]].x ; s2 += poly[stak[(i+1)%n]].y * poly[stak[(i+2)%n]].x ; } return fabs( s1 - s2 ) / 2 ; } void process() { gramham() ;//保存好凸包的点在stack[]中 outarea = farea_poly( top+1, point ) ; } double ans; void output() { printf("%.4lf\n", outarea / ans) ; } void solve() { srand(time(0)); int n; scanf("%d", &n); inn = n; for(int i=1; i<=n; i++) { scanf("%lf%lf", &pot[i].x, &pot[i].y); point[i - 1].x = pot[i].x; point[i - 1].y = pot[i].y; } random_shuffle(pot+1, pot+n+1); vec o; double r2 = 0; for(int i=1; i<=n; i++) { if((pot[i]-o).len2() > r2) { o = pot[i], r2 = 0; for(int j=1; j<i; j++) { if((pot[j]-o).len2() > r2) { o = (pot[i]+pot[j])/2, r2 = (pot[j]-o).len2(); for(int k=1; k<j; k++) { if((pot[k]-o).len2() > r2) { o = circle(pot[i], pot[j], pot[k]), r2 = (pot[k]-o).len2(); } } } } } } ans = r2 * M_PI; printf("%.4lf %.4lf\n", o.x, o.y); input(); process(); output(); puts("Humanity will prevail, and the civilization of Earth will endure!"); }
G.scx的记忆碎片 https://ac.nowcoder.com/acm/contest/71344/G
写出状态转移方程后使用矩阵快速幂加速状态转移。
using mat = array<array<int, 101>, 101>; const int mod = 998244353; mat multiply(mat a, mat b) { mat r = {0}; for (int i = 0; i <= 100; i++) { for (int j = 0; j <= 100; j++) { for (int k = 0; k <= 100; k++) { r[i][k] = (r[i][k] + 1LL * a[i][j] * b[j][k]) % mod; } } } return r; } void solve() { ll m; cin >> m; int k; cin >> k; mat ans = {0}; for (int i = 0; i <= 100; i++) ans[i][i] = 1; mat cur = {0}; for (int i = 0; i <= 100; i++) { for (int j = 0; j <= 9 && i - j >= 0; j++) { cur[i][i - j] = 1; } } while (m) { if (m % 2) ans = multiply(ans, cur); cur = multiply(cur, cur); m /= 2; } if (k == 1) { ans[k][0]++; ans[k][0] %= mod; } cout << ans[k][0] << endl; }
H. 别卷了,总力战!https://ac.nowcoder.com/acm/contest/71344/H
先计算出一天的答案,然后直接乘天数(期望的可加性)。
void solve() { int k, t; cin >> k >> t; long double p; cin >> p; long double ans = 0, pre = 1; for (int i = 1; i <= t; i++) { ans += pre * p; pre *= (1 - p); } ans *= k; cout << fixed << setprecision(4) << ans << endl; }
J.阶乘之乘 https://ac.nowcoder.com/acm/contest/71344/J
发现去掉所有末尾零也就是同时去掉一对 2 和 5 因子,直到不存在 2 因子或者不存在 5 因子为止,同时保留 K 位也就是模 10^K 的意思。
可以把先计算除了 2 和 5 因子的答案,然后把多出来的 2 因子乘上去, 因为 2 因子数量大于等于 5 因子数量,注意取模。
int f[2][9], f2[2][9], m[9]; vector<pair<int, int>> q[int(5e6 + 1)]; int ans[int(1e5 + 1)]; ll quickPow(ll a, ll b, ll m) { ll r = 1; while (b) { if (b % 2) r = (r * a) % m; a = (a * a) % m; b /= 2; } return r; } void init() { m[0] = 1; for (int i = 1; i <= 8; i++) m[i] = m[i - 1] * 10; for (int i = 1; i <= 8; i++) f[0][i] = f2[0][i] = 1; ll a = 0, b = 0; for (int i = 1; i <= 5e6; i++) { int cur = i % 2, pre = cur ^ 1; int t = i; while (t % 2 == 0) { a++; t /= 2; } while (t % 5 == 0) { a--; t /= 5; } b += a; for (int j = 1; j <= 8; j++) { f2[cur][j] = (1LL * f2[pre][j] * t) % m[j]; f[cur][j] = (1LL * f[pre][j] * f2[cur][j]) % m[j]; } for (auto [k, id] : q[i]) { ans[id] = f[cur][k]; ans[id] = (1LL * ans[id] * quickPow(2, b, m[k])) % m[k]; } } } void solve() { int t; cin >> t; vector<int> k(t + 1); for (int i = 1; i <= t; i++) { int n; cin >> n >> k[i]; q[n].push_back({k[i], i}); } init(); for (int i = 1; i <= t; i++) { string a(to_string(ans[i])); for (int j = 1; j <= k[i] - (int) a.size(); j++) { cout << '0'; } cout << a << endl; } }
K.寂寞沙洲冷 https://ac.nowcoder.com/acm/contest/71344/K
经典分数规划,没学过的可以看这里 https://oi-wiki.org/misc/frac-programming/
二分查找答案 ans,设当前的二分中点为 mid,对选用的边的客流量之和 sum(v[i]) 比上花费之和 sum(c[i])>=mid进行移项,得到 sum(v[i] - mid * c[i]) >= 0。
容易发现可以分开计算每一条边的贡献 W, 然后跑最小生成树算法,在森林内恰好有 K 棵树时,判定是否合法并移动左右边界。
void solve() { int n, m, k; cin >> n >> m >> k; k = n - 1 - (k - 1); vector<array<int, 4>> e(m + 1); for (int i = 1; i <= m; i++) { cin >> e[i][0] >> e[i][1] >> e[i][2] >> e[i][3]; } //sum(e[i][2]) / sum(e[i][3]) >= ans //sum(e[i][2]) >= ans * sum(e[i][3]) //sum(e[i][2]) - ans * sum(e[i][3]) >= 0 //sum(e[i][2] - ans * e[i][3]) >= 0 cout << fixed << setprecision(6); long double left = 0, right = 1e4, ans = 0; for (int i = 1; i <= 40; i++) { long double mid = left + (right - left) / 2; sort(e.begin() + 1, e.end(), [&](array<int, 4> a, array<int, 4> b) -> int { return (a[2] - 1LL * mid * a[3]) > (b[2] - 1LL * mid * b[3]); }); int cnt = 0; vector<int> root(n + 1); iota(root.begin() + 1, root.end(), 1); function<int(int)> find = [&](int x) -> int { if (root[x] != x) root[x] = find(root[x]); return root[x]; }; long double sum = 0; for (int j = 1; j <= m && cnt < k; j++) { auto [x, y, v, c] = e[j]; x = find(x), y = find(y); if (x == y) continue; sum += v - 1LL * mid * c; cnt++; root[x] = y; } if (sum >= 0) { left = mid; ans = mid; } else { right = mid; } } cout << ans << endl; }
L. 动手学自然语言处理 https://ac.nowcoder.com/acm/contest/71344/L
对每个状态 S, 记录它所有后继状态里面出现次数最大的那个。
看成从每个句子开头进行匹配了,写了个字典树,喜提两发 WA。
void solve() { map<string, map<string, int>> tr; map<string, string> mx; int n; cin >> n; for (int i = 1; i <= n; i++) { int k; cin >> k; string pre; cin >> pre; for (int j = 2; j <= k; j++) { string cur; cin >> cur; int cnt = ++tr[pre][cur]; int mxcnt = tr[pre][mx[pre]]; if (cnt > mxcnt || (cnt == mxcnt && mx[pre] > cur)) { mx[pre] = cur; } pre = cur; } } int m; cin >> m; for (int i = 1; i <= m; i++) { string first; cin >> first; cout << first << ' '; for (int j = 2; j <= 21; j++) { if (!mx.count(first)) break; first = mx[first]; cout << first << ' '; } cout << endl; } }
M.终不似,少年游 https://ac.nowcoder.com/acm/contest/71344/M
按题意进行模拟。
void solve() { string s; cin >> s; int ans = 0; for (int i = 2; i < s.size(); i++) { string c(s.substr(i - 2, 3)); if (c == "hzy") ans += 3; if (c == "zzy") ans += 3; if (c == "syh") ans += 3; } cout << ans << endl; }