2022-SZUACM招新 训练赛2

2022-SZUACM招新 训练赛2

https://vjudge.net/contest/544906#overview
下午打了一下,稍微记录一波,有一些蛮有意思的小题。

A - Array

https://codeforces.com/problemset/problem/57/C
这是组合数学的一个小模型。
由于对组合数学的理解不够,讲不出来(感觉非常的抽象啊),这里贴一个洛谷的题姐

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 1e6 + 5, p = 1000000007;
ll n, ans;
ll fact[N], infact[N];

ll qmi(ll a, ll k) {
	ll ans = 1;
	while (k) {
		if (k & 1)	ans = ans * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return ans;
}

int main () {
    cin >> n;
    ll fz = 1, fm = 1;
    for (int i = 1; i < n; i++)     (fm *= i) %= p;
    for (int i = n + 1; i < 2 * n; i++)     (fz *= i) %= p;
    cout << (2 * fz * qmi (fm, p - 2) - n + p) % p;
    //cout << (2 * C (2 * n - 1, n) - n) % p;
}

//推式子
//去重

B - Divisible Substring

https://atcoder.jp/contests/abc158/tasks/abc158_e?lang=en
这个也没想到,是一个很巧妙的转化。

洛谷题解

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 10005;
ll ans, cnt[N];

void solve () {
    int n, p;
    string s;
    cin >> n >> p >> s;
    if (p == 2 || p == 5) {
        for (int i = 0; i < s.size (); i++) {
            if ((s[i] - '0') % p == 0)  ans += (i + 1);
        }
    }
    else {
        ll dx = 1, sum = 0;
        cnt[0] = 1; //init模数0因子
        for (int i = s.size () - 1; i >= 0; i--) {
            sum += dx * (s[i] - '0');
            sum %= p;
            ans += cnt[sum];
            cnt[sum] ++;
            dx = (dx * 10) % p;
            //cout << sum << ' ';
        }
    }
    cout << ans << endl;
}

int main () {
    solve ();
}

//串s,t末尾数字余数相等,且一个是另一个的末尾,则他们相减后得到的串符合要求
//(a+x)-(b+x)=10^x+c
//2,5是例外(是10的因子)

C - Can you answer these queries III

https://www.spoj.com/problems/GSS3/en/
线段树维护区间最大和。
比较典的一道题,按照这篇题解图中描述的去分情况维护即可。

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 5e5 + 5;
int n, m, k, x, y;
int a[N];

struct tree{
    int l, r, sum;
    int lmax, rmax, tmax;//最大前后缀和, 最大子段和
}st[4 * N];

void pushup (tree &u, tree &l, tree &r){
    u.sum = l.sum + r.sum;
    u.lmax = max (l.lmax, l.sum + r.lmax);//左半段中的最大 or 左半段和+右半段中的最大
    u.rmax = max (r.rmax, r.sum + l.rmax);//右半段中的最大 or 右半段和+左半段中的最大
    u.tmax = max (max (l.tmax, r.tmax), l.rmax + r.lmax);//左半段中的最大 or 右半段中的最大 or 左半段后缀和+右半段前缀和
}

void pushup (int u){
    pushup (st[u], st[u << 1], st[u << 1 | 1]);
}

void build (int u, int l, int r){
    if (l == r){
        st[u] = {l, r, a[r], a[r], a[r], a[r]};
        return;
    }
    st[u] ={l, r};
    int mid = l + r >> 1;
    build (u << 1, l, mid), build (u << 1 | 1, mid + 1, r);
    pushup (u);
}

void modify (int u, int x, int v){
    if (st[u].l == x && st[u].r == x)
        st[u] = {x, x, v, v, v, v};
    else{
        int mid = st[u].l + st[u].r >> 1;
        if (x <= mid)
            modify (u << 1, x, v);
        else    
            modify (u << 1 | 1, x, v);
        pushup (u);
    }
}

tree query (int u, int l, int r){
    if (st[u].l >= l && st[u].r <= r)
        return st[u];
    int mid = st[u].l + st[u].r >> 1;
    if (r <= mid)
        return query (u << 1, l, r);
    else if (l > mid)
        return query (u << 1 | 1, l, r);
    else{
        auto le = query (u << 1, l, r);
        auto ri = query (u << 1 | 1, l, r);
        tree ans;
        pushup (ans, le, ri);
        return ans;
    }

}

int main(){
    cin >> n;
    for (int i = 1; i <= n; i ++)    cin >> a[i];
    build (1, 1, n);
    cin >> m;
    while (m --){
        cin >> k >> x >> y;
        if (k == 0)    modify (1, x, y);
        else{
            if (x > y)    swap (x, y);
            cout << query (1, x, y).tmax << endl;
        }
        
    }

}
//维护和
//对于横跨两区间的最大子段和 = 左子区间最大后缀 + 右子区间最大前缀

D - Nun Heh Heh Aaaaaaaaaaa

https://acm.hdu.edu.cn/showproblem.php?pid=7131
以为是什么数据结构结果是比较典型的线性dp(啊果然dp水平还是太烂了www)
注:为了方便书写,以下字符串默认下标从1开始。
定义状态 \(f_{i,j}\),表示当前串的前 \(i\) 位中匹配上 \(t\) 的第 \(j\) 为的数量。同时利用 \(a_i\) 记录当前串的 \(i\) 位以后有 \(a_i\) 个字符 '\(a\)'。
转移:\(f_{i,j}=f_{i-1,j}\)(无条件),若匹配上 \(t\) 的第 \(j\) 位则有 \(f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\)(多匹配了一位)
最终答案就是 \(\sum_{i=1}^n f_{i,8}*(2^{a_i}-1)\),当且仅当 \(s_i=\)'\(h\)'
进行一些细节的解释:因为当前位是 \(h\),则保证了末位(第9位匹配)匹配上,因此是乘上 \(f_{i,8}\);至于后面字符 \(a\) 的贡献,手推一下也可以得出结论:

然后注意随时取模,防止爆了。

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 1e5 + 5, mod = 998244353;
ll f[N][15], a[N], pw[N];
string t = "$nunhehheh";

void solve () {
    string s;
    cin >> s;
    int n = s.size ();
    s = ' ' + s;
    for (int i = 0; i <= n + 1; i++) { //init
        a[i] = 0, f[i][0] = 1; //一个也没匹配上的个数是1
        for (int j = 1; j <= 9; j++)    f[i][j] = 0;
    }
    for (int i = n; i; i--) {
        a[i] = a[i + 1];
        if (s[i] == 'a')    (a[i] += 1) %= mod;
    }

    //for (int i = 1; i <= n; i++)    cout << a[i] << ' ';cout << endl;

    //match
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 9; j++) {
            f[i][j] = f[i-1][j] % mod;
            if (s[i] == t[j])   (f[i][j] += f[i-1][j-1]) %= mod;
        }
    }
    
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        if (s[i] == 'h')    (ans += f[i][8] * (pw[a[i]] - 1) % mod) %= mod;
    }
    cout << ans << endl;
}

int main () {
    pw[0] = 1;
    for (int i = 1; i <= 1e5; i++)  pw[i] = pw[i-1] * 2 % mod;
    int t;
    cin >> t;
    while (t --)    solve ();
}

//求子序列p的个数
//dp[i][j]:s的第i位和p匹配到j
//a[i]:i后面有a[i]个'a'
//ans += dp[i][8] * (2^a[i]-1)

E - GCD

https://acm.hdu.edu.cn/showproblem.php?pid=2588
数论小题,关键在于转化。
题目是要找到 \(1-n\) 中满足的 \(gcd(n,x)\geq m\) 的数 \(x\) 的个数。

分析

首先这个 \(x\) 一定与 \(n\) 有共同的因子,则设 \(gcd(n,x)=d\),有 \(n=pd, x=qd\)\(p,q\) 互质。
因此问题转化为,对于 \(n\)\(\geq m\) 的因子 \(d\),求 \(\leq p=\frac nd\) 的数的个数。不难发现,这符合欧拉函数的定义(即求 \(\phi(\frac nd)\)):

(摘自oi-wiki)

#include <bits/stdc++.h>
#define ll long long

using namespace std;

ll phi (ll n) {
    ll ans = n;
    for (ll i = 2; i*i <= n; i++) {
        if (n % i == 0) {
            ans = ans / i * (i - 1); //*(1-1/i)
            while (n % i == 0)  n /= i;
        }
    }
    if (n > 1)  ans = ans / n * (n - 1); //还剩下就再乘
    return ans;
}

void solve () {
    ll n, m;
    cin >> n >> m;
    // for (ll i = 1; i <= n; i++)    cout << __gcd (i, n) << ' ';
    // cout << endl;
    ll ans = 0;
    for (ll i = 1; i * i <= n; i++) {
        if (n % i)      continue;
        if (i * i == n && i >= m)   ans += phi (i);
        else if (i * i < n) {
            if (i >= m)     ans += phi (n / i);
            if (n / i >= m) ans += phi (i);
        } 
    }
    cout << ans << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --)    solve ();
}

F - 123-sequence

https://codeforces.com/problemset/problem/52/A
签到

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 5;
int a[N], n;

int main () {
    map<int, int> mp;
    cin >> n;
    for (int i = 0; i  < n; i++) {
        int x;
        cin >> x;
        mp[x] ++;
    }
    cout << n - max ({mp[1], mp[2], mp[3]});
}
posted @ 2023-02-26 19:48  Sakana~  阅读(152)  评论(0编辑  收藏  举报