The 2022 Hangzhou Normal U Summer Trials
6 题
vp 排名 54/253
铜,现场排名22/152
银
感觉浙江省的 acm 强度继承了浙江 oi 强度还是很高的
A. 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;
}
B. 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;
}
C. Check Problems
有无穷多个问题和n个人,每个人会从\(a_i\)个问题开始解决,每一秒只能解决一个问题,一个问题也只能没解决一次,问t秒后一共可以解决多少个问题。
从题目发现\(a_i\) 是递增的,同时\(a_{i+1}-a_i\)也是递增的。那么我们令\(b_i=a_{i+1}-a_{i}\)那么当\(t<a_i\)是时i个人解决了t个问题,当\(t\ge a_i\) 时第i个人解决了\(b_i\)个问题。这样的话对于每一个询问二分一下分界线的位子就可以\(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;
}
D. 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;
}
E. 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;
}
F. 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;
}
I. IHI's Homework
换一个思路来理解这道题,有n 个盒子和 s 个球,要把 s 个球放到盒子中,可以有球不放入任何一个盒子中。第 i 个盒子中的球不少于\(x_i\)个,每一次会修改一个\(x_i\),问有多少种结果。
首先把每个盒子中放入\(x_i\)个球,然后剩下 t 个球。可以把这 t 个球中的任意个放入 n 个盒子。
然后经典的球盒问题,n 个球无区别,m 个盒子有区别,允许有空盒,方案数是\(C_{m+n-1}^n\)。
可以知道答案就是
可以发现对于相同的 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;
}
H. Optimal Biking Strategy
这道题就是 DP ,f[i][j]
表示前i 个公交站花费 j 最少可以走多少步数,对于花费\(x\)元至少可以走\(x\times 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;
}
J. 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;
}