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\)

可以知道答案就是

\[\sum_{i=1}^{t}C_{n+i-1}^{i} \]

可以发现对于相同的 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;
}
posted @ 2022-09-01 21:22  PHarr  阅读(230)  评论(0编辑  收藏  举报