The 2022 Hangzhou Normal U Summer Trials

6 题

vp 排名 54/253 铜,现场排名22/152

感觉浙江省的 acm 强度继承了浙江 oi 强度还是很高的

1|0A. Hello, ACMer!


这题就是找到hznu的个数

#include<bits/stdc++.h> using namespace std; int32_t main() { string s; cin >> s; int cnt = 0; for( int i = 0 ; i + 3 < s.size() ; i ++ ){ if( s[i] == 'h' && s[i+1] == 'z' && s[i+2] == 'n' && s[i+3] == 'u' ) cnt ++; } cout << cnt << "\n"; return 0; }

2|0B. New String


这题是给出一个排序,表示的字母的相对顺序。

把字符串转化然后在排序就好。

#include<bits/stdc++.h> using namespace std; const int N = 1e3+5; struct node{ string a ; string b; node() { a = "" , b = "";} friend bool operator < ( node x , node y ){ return x.a < y.a; } } s[N]; int main(){ string st; char mp[30]; cin >> st; for( int i = 0 ; i < 26 ; i ++ ){ mp[ st[i] - 'a' ] = i+'a'; } int n , m; cin >> n; for( int i = 1 ; i <= n ; i ++ ) { cin >> s[i].b; for( auto it : s[i].b ) s[i].a += mp[ it - 'a' ]; } sort( s + 1 , s + 1 + n ); cin >> m; cout << s[m].b; }

3|0C. Check Problems


有无穷多个问题和n个人,每个人会从ai个问题开始解决,每一秒只能解决一个问题,一个问题也只能没解决一次,问t秒后一共可以解决多少个问题。

从题目发现ai 是递增的,同时ai+1ai也是递增的。那么我们令bi=ai+1ai那么当t<ai是时i个人解决了t个问题,当tai 时第i个人解决了bi个问题。这样的话对于每一个询问二分一下分界线的位子就可以O(1)的算出解决了多少问题。

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 5e5+5; int n , a[N] , b[N]; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } int32_t main() { n = read(); for( int i = 1 ; i <= n ; i ++ ) a[i] = read(); for( int i = 1 ; i < n ; i ++ ) b[i] = a[i+1] - a[i]; for( int t , x , m = read() ; m ; m -- ){ t = read(); x = lower_bound( b+1 , b+n , t ) - b; cout << a[x] - a[1] + ( n - x + 1 ) * t << "\n"; } return 0; }

4|0D. Tree Problem


给一个树,每次询问一个点,问树上有多少对点的简答路径经过了这个点。

对于每一个,我们把她当成树的根,那么他的任意一个子树中任意选择两个点,这两个点的简单路径都不经过这个点。

所以要统计出每一个点的每一个子树的大小,这个用一遍dfs就可以了。然后在套一些组合数就好了。

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e5+5; vector< int > e[N] , son[N] ; int sz[N] , n , sum , res[N]; bool vis[N]; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } void dfs( int u ){ vis[u] = sz[u] = 1; for( auto v : e[u] ){ if( vis[v] ) continue; dfs(v); sz[u] += sz[v]; son[u].push_back( sz[v] ); } son[u].push_back( n - sz[u] ); } int32_t main() { n = read(); for( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , e[u].push_back(v) , e[v].push_back(u); dfs( 1 ); sum = (n * n - n ) / 2; for( int i = 1 ; i <= n ; i ++ ){ res[i] = sum; for( auto it : son[i] ) if( it >= 2 ) res[i] -= ( it * it - it ) / 2; } for( int m = read() , x; m ; m -- ){ x = read(); cout << res[x] << "\n"; } return 0; }

5|0E. Easy Problem


有AB两个人,他们每次都会向相同的地方移动,但是他们中的某个人遇到边界或者障碍是不会继续走的。问最少多少步他们就会相遇。

因为n很小,所以直接bfs就好了。然后加一点记忆化剪枝。

#include<bits/stdc++.h> using namespace std; typedef tuple<int,int,int,int> node; const int N = 55; bitset<N> mp[N]; int n; const int dx[] = { 0 , 0 , 1 , -1 } , dy[] = { 1 , -1 , 0 , 0 }; queue< node > q; map< node , int > dep; bool check( int x , int y ){ if( x < 0 || y < 0 || x >= n || y >= n ) return false; if( mp[x][y] ) return false; return true; } int main(){ cin >> n; int ax , ay , bx , by; for( int i = 0 ; i < n ; i ++ ){ string s; cin >> s; for( int j = 0 ; j < n ; j ++ ) if( s[j] == '*' ) mp[i][j] = 1; else if( s[j] == 'a' ) ax = i , ay = j; else if( s[j] == 'b' ) bx = i , by = j; } dep[ { ax , ay , bx , by } ] = 0; q.push( { ax , ay , bx , by } ); while( q.size() ){ auto [ ax , ay , bx , by ] = q.front() ; q.pop(); if( ax == bx && ay == by ){ cout << dep[ { ax , ay , bx , by } ] << "\n"; return 0; } for( int i = 0 ; i < 4 ; i ++ ){ int fax = ax +dx[i] , fay = ay + dy[i] , fbx = bx + dx[i] , fby = by + dy[i]; if( !check( fax , fay ) ) fax = ax , fay = ay; if( !check( fbx , fby ) ) fbx = bx , fby = by; if( dep.find( { fax , fay , fbx , fby } ) != dep.end() ) continue; q.push( { fax , fay , fbx , fby } ) ; dep[ { fax , fay , fbx , fby } ] = dep[ { ax ,ay , bx , by } ] + 1; } } cout << "no solution\n"; return 0; }

6|0F. Subarrays


这道题是原题1230. K倍区间

pre[i]表示前缀和,如果[l,r] 的和是 k 的倍数,那么一定有pre[l+1]%k == pre[r]%k,那么统计一下前缀和对 k 取余的结果就好。

#include<bits/stdc++.h> #define int long long using namespace std; int read(){ int x = 0 , ch = getchar(); while( ch < '0' || ch > '9' ) ch = getchar(); while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar(); return x; } int32_t main(){ int n = read() , m = read() , res = 0; map< int , int > p; for( int x = 0 ; n ; n -- ) x = ( x + read() ) % m , res += p[x] , p[x] ++; cout << res + p[0] << "\n"; return 0; }

7|0I. IHI's Homework


换一个思路来理解这道题,有n 个盒子和 s 个球,要把 s 个球放到盒子中,可以有球不放入任何一个盒子中。第 i 个盒子中的球不少于xi个,每一次会修改一个xi,问有多少种结果。

首先把每个盒子中放入xi个球,然后剩下 t 个球。可以把这 t 个球中的任意个放入 n 个盒子。

然后经典的球盒问题,n 个球无区别,m 个盒子有区别,允许有空盒,方案数是Cm+n1n

可以知道答案就是

i=1tCn+i1i

可以发现对于相同的 t 他的答案是相同的,可以先预处理出阶乘和阶乘的逆元,这样就可以O(1)的计算组合数,然后在求一个前缀和,每次就可以O(1)的回答询问了

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5+5 , mod = 1e9+7; int n , s , m , a[N] , p[N] , sum ; int jie[N*2] , jie_inv[N*2]; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } int power( int x , int y ){ int ans = 1; while( y ){ if( y & 1 ) ans = ans * x % mod; x = x * x % mod; y >>= 1; } return ans; } int invs( int x ){ return power( x , mod - 2 ); } int C( int n , int m ){ if( n == 0 ) return 1; return jie[m] * jie_inv[m-n] % mod * jie_inv[n] % mod; } int32_t main() { n = read() , s = read() , m = read(); for( int i = 1 ; i <= n ; i ++ ) a[i] = read() , sum += a[i]; jie[0] = 1 , jie_inv[0] = invs( jie[0] ); for( int i = 1 ; i <= n + s ; i ++ ) jie[i] = jie[i-1] * i % mod , jie_inv[i] = invs( jie[i] ); for( int i = 0 ; i <= s ; i ++ ) p[i] = C( i , n + i - 1 ); for( int i = 1 ; i <= s ; i ++ ) p[i] = (p[i] + p[i-1]) % mod; for( int x , y ; m ; m -- ){ x = read() , y = read(); sum = sum - a[x] + y , a[x] = y; if( sum > s ) cout << "0\n"; else cout << p[ s - sum ] << "\n"; } return 0; }

8|0H. Optimal Biking Strategy


这道题就是 DP ,f[i][j]表示前i 个公交站花费 j 最少可以走多少步数,对于花费x元至少可以走x×s距离,所以通过二分找到在这个范围中最远的公交站即可

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e6+5; int n , p , s , k; int a[N] , f[N][6]; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } int32_t main() { n = read() , p = read() , s = read(); for( int i = 1 ; i <= n ; i ++ ) a[i] = read(); k = read(); for( int i = 1 ; i <= n ; i ++ ) for( int j = 0 ; j < k ; j ++ ) f[i][j] = INT_MAX; for( int i = 1 ; i <= n; i ++ ){ f[i][0] = f[i-1][0] + a[i] - a[i-1]; for( int j = 1 ; j <= k ; j ++ ){ f[i][j] = f[i-1][j] + a[i] - a[i-1]; for( int x = 0 , dis , idx ; x <= j ; x ++ ){ dis = x * s; idx = lower_bound( a + 1 , a + 1 + i , a[i] - dis ) - a; f[i][j] = min( f[i][j] , f[idx][ j - x ] ); } } } int res = INT_MAX; for( int i = 1 ; i <= n ; i ++ ) res = min( res , f[i][k] + p - a[i] ); cout << res << "\n"; return 0; }

9|0J. IHI's Magic String


这道题如果正着坐会非常的麻烦因为每一次修改都是 O(n)

所以考虑倒过来做。

如果把a修改成b那么在之后的添加中添加a就可以直接用添加b来替代。

mp[i]表示添加i时应该添加mp[i],初始时候mp[i]=i

那么,如果先把b修改成a,在把c修改成b,实际上可以直接把c改成a。这样的话我们可以用类似并查集路径压缩的方式来解决这道题,就是mp[c] = mp[b]

剩余就是删除的问题,因为是倒过来做,删除一个其实就是下一次添加,这里用一个懒标记就好了。

#include<bits/stdc++.h> using namespace std; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } map< char , char > mp; typedef tuple< int , char , char > node; int32_t main() { int n = read(); vector<node> v; for( char i = 'a' ; i <= 'z' ; i ++ ) mp[i] = i; for( auto [ op , a , b ] = node() ; n ; n -- ){ op = read(); if( op == 1 ) cin >> a , b = '*'; else if( op == 2 ) a = b = '*'; else cin >> a >> b; v.push_back( { op , a , b } ); } string res = ""; int del = 0; for( int i = v.size() - 1 ; i >= 0 ; i -- ){ auto op = get<0>(v[i]); if( op == 1 ){ if( del ) del --; else res += mp[ get<1>(v[i]) ]; } else if( op == 2 ) del ++; else{ char a = get<1>(v[i]) , b = get<2>(v[i]); mp[a] = mp[b]; } } reverse( res.begin() , res.end() ); if( res.empty() ) cout << "The final string is empty\n"; else cout << res << "\n"; return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/16647878.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(250)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示