SCAU算法设计与分析 —— 递归与分治

4 阅读20分钟

By 三石吖 2026.2

  • 最后一题没写

2.递归与分治

8594 有重复元素的排列问题

重点是知道有重复元素排列问题的去重方式,至于产生排列的顺序,会一种即可,本题做法了解即可

  • 本题做法生成排列的顺序未必是字典序最优的
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){   
    int n;
    std::string s;
    std::cin >> n >> s;
    int tot = 0;
    auto dfs = [&] (auto &&self, int inx) -> void {
        if(inx >= n){
            std::cout << s << "\n";
            tot++;
            return;
        }

        auto check = [&] (int start, int end){
            for(int i = start; i < end; i++){
                if(s[i] == s[end]){
                    return true;
                }
            }
            return false;
        };

        for(int i = inx; i < n; i++){
            if(check(inx, i)){
                continue;
            }
            std::swap(s[i], s[inx]);
            self(self, inx + 1);
            std::swap(s[i], s[inx]);
        }
    };

    dfs(dfs, 0);
    std::cout << tot << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17088 分治法求众数

不会分治法。

排序法:

先排序然后双指针扫一遍

  • 时间复杂度O(n)O(n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){   
    int n;
    std::cin >> n;
    std::vector<int>a(n);
    for(int i = 0; i < n; i++){
        std::cin >> a[i];
    }
    std::sort(a.begin(), a.end());
    int cnt = 0, ans;
    for(int i = 0, j; i < n; i = j){
        j = i;  
        while(j < n && a[j] == a[i]){
            j++;
        }
        if(j - i > cnt){
            cnt = j - i;
            ans = a[i];
        }
    }
    std::cout << ans << " " << cnt << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

9714 圣诞礼物

发现好像可以直接dpdp

考虑用dp[i][j]dp[i][j] 表示将ii个礼物放进jj个盒子,且每个盒子都不空的方法数

由于限制了每个盒子都不空,我们可以先往每个盒子中放进一个礼物,那么还剩iji - j个礼物,这iji - j个礼物可以放进1,2...j1,2...j个盒子中

于是得到了状态转移方程:dp[i][j]=k=0jdp[ij][k]dp[i][j] = \sum_{k=0}^{j}dp[i-j][k]

初始化dp[0][0]=1dp[0][0] = 1,答案就是j=0min(m,n)dp[m][j]\sum_{j=0}^{min(m,n)}dp[m][j](因为mm个礼物最多放入mm个盒子,因此可以优化掉)

  • 时间复杂度O(m×n×n)O(m \times n \times n),此处的nnmin(n,m)min(n, m)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){ 
    int m, n;
    std::cin >> m >> n;
    n = std::min(m, n);
    std::vector<std::vector<int>>dp(m + 1, std::vector<int>(n + 1));
    dp[0][0] = 1;
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= std::min(n, i); j++){
            for(int k = 0; k <= j ; k++){
                dp[i][j] += dp[i - j][k];
            }
        }
    }
    int ans = 0;
    for(int i = 0 ; i <= n; i++){
        ans += dp[m][i];
    }
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

求和部分可以用前缀和优化,这样每次加法都是O(1)O(1)

  • 时间复杂度O(m×n)O(m \times n),其中n=min(n,m)n = min(n,m)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){ 
    int m, n;
    std::cin >> m >> n;
    n = std::min(m, n);
    std::vector<std::vector<int>>dp(m + 1, std::vector<int>(n + 1));
    std::vector<std::vector<int>>pre(m + 1, std::vector<int>(n + 1));
    dp[0][0] = 1;
    for(int i = 0; i <= n; i++){
        pre[0][i] = 1;
    }
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= std::min(n, i); j++){
            dp[i][j] += pre[i - j][j];
        }
        for(int j = 1; j <= n; j++){
            pre[i][j] = pre[i][j - 1] + dp[i][j];
        }
    }
    int ans = 0;
    for(int i = 0 ; i <= n; i++){
        ans += dp[m][i];
    }
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

9718 整数因子分解

f(n)f(n)nn的分解式数量,依次枚举nn的大于11的因数,将它们固定在第一位,问题转化为求i=1mf(nxi)(n%xi=0)\sum_{i=1}^{m}f(\frac{n}{x_i}) \quad (n \% x_i = 0)

问题可以拆解为若干子问题,考虑递归求解

其中涉及大量重复计算,可用记忆化搜索优化

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){ 
    int n;
    std::cin >> n;
    std::vector<int>f(n + 1);
    f[1] = 1;

    auto dfs = [&](auto &&self, int cur) -> int {
        if(f[cur]){
            return f[cur];
        }

        for(int i = 2; i <= std::sqrt(cur); i++){
            if(cur % i){
                continue;
            }
            f[cur] += self(self, cur / i);
            if(i * i != cur){
                f[cur] += self(self, i);
            }
        }
        f[cur]++;
        return f[cur];
    };

    int ans = dfs(dfs, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

10302 整数的特殊划分

好像有很好的做法!

由于22的幂次增长极快,nn以内的22的幂次其实并不多,就算nn1e51e5级别,那也只会有1717种不同的22的幂次

换句话说,我们可以先在常数级的复杂度内找到所有小于nn22的幂次,设其数量为mm

现在问题变成了 给定mm个不同的数,以及一个正整数nn,每个数可以重复取,问有多少种组成nn的方法数

或许这还不够直观,那么可以看成:

给定mm个不同的物品,以及一个容量nn,每个物品可以重复取,问有多少种容量为nn的填充方式

发现显然就是 完全背包 的板子题,直接dpdp即可

  • 时间复杂度O(nlogn)O(nlogn)lognlogn为物品数量
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

constexpr int mod = 1000000000;
void solve(){
    int n;
    std::cin >> n;
    std::vector<int>a;
    for(int i = 1; i <= n; i *= 2){
        a.push_back(i);
    }
    int m = a.size();
    std::vector<i64>dp(n + 1);
    dp[0] = 1;
    for(int i = 0; i < m; i++){
        for(int j = a[i]; j <= n; j++){
            dp[j] = (dp[j] + dp[j - a[i]]) % mod;
        }
    }
    std::cout << dp[n] << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

10304 平面域着色

首先判掉不可能的情况:

k=1k=1n>1n \gt 1

k=2k=2nn为奇数

dp[i]dp[i]表示用kk种颜色给ii个区域染色的方法数

①若第i1i-1个区域和第11个区域同色,等价于给前i2i-2个区域染色的方法数,此时第ii个区域有k1k-1种染色方式,即为dp[i2]×(k1)dp[i-2] \times (k-1)

②若第i1i-1个区域和第11个区域不同色,此时第ii个区域有k2k-2种染色方式,为dp[i1]×(k2)dp[i-1] \times (k-2)

即:dp[i]=dp[i2]×(k1)+dp[i1]×(k2)dp[i]=dp[i-2] \times (k-1) + dp[i-1] \times (k-2)

要初始化前三个数

  • 时间复杂度O(n)O(n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n, k;
    std::cin >> n >> k;
    if(k == 1 && n > 1){
        std::cout << "0\n";
        return;
    }else if(k == 2 && n % 2){
        std::cout << "0\n";
        return;
    }

    if(n == 1){
        std::cout << k << "\n";
        return;
    }else if(n == 2){
        std::cout << k * (k - 1) << "\n";
        return;
    }else if(n == 3){
        std::cout << k * (k - 1) * (k - 2) << "\n";
        return;
    }

    std::vector<i64>dp(n + 1);
    dp[1] = k, dp[2] = k * (k - 1), dp[3] = k * (k - 1) * (k - 2);
    for(int i = 4; i <= n; i++){
        dp[i] = dp[i - 2] * (k - 1) + dp[i - 1] * (k - 2);
    }
    std::cout << dp[n] << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

10343 划分凸多边形

给每个顶点按顺时针顺序标上1,2,..n1,2,..n,顶点1,n,i1,n,i可以将凸多边形划分成一个三角形,一个ii边形,一个ni+1n-i+1边形,然后分解成了两个子问题,递归求解即可

  • 时间复杂度O(n2)O(n^2)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    if(n < 3){
        std::cout << "No answer\n";
        return;
    }
    std::vector<int>f(n + 1 , -1);
    f[2] = f[3] = 1;

    auto dfs = [&](auto &&self, int x) -> int {

        if(f[x] != -1){
            return f[x];
        }

        f[x] = 0;
        for(int i = 2; i < x; i++){
            f[x] += self(self, i) * self(self, x - i + 1);            
        }

        return f[x];
    };  

    int ans = dfs(dfs, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

10344 矩阵连乘积的加括号方式数

卡特兰数太难

可以看看oi-wiki.org/math/combin…

本题就是求卡特兰数

  • 时间复杂度O(n2)O(n^2)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    if(n <= 2){
        std::cout << "1\n";
        return;
    }else if(n == 3){
        std::cout << "2\n";
        return;
    }

    std::vector<int>f(n + 1);
    f[1] = f[2] = 1;
    f[3] = 2;

    auto dfs = [&](auto &&self, int x) -> int {
        if(f[x]){
            return f[x];
        }

        for(int i = 1; i < x; i++){
            f[x] += self(self, i) * self(self, x - i);
        }
        return f[x];
    };

    int ans = dfs(dfs, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11073最热门的KK个搜索串

维护一个大小为kk的小顶堆,当堆的大小大于kk时,弹出堆顶

  • 时间复杂度O(nlogk)O(nlogk)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){   
    int n, k;
    std::cin >> n >> k;
    std::priority_queue<int, std::vector<int>, std::greater<>>que;
    for(int i = 0; i < n; i++){
        int x;
        std::cin >> x;
        que.push(x);
        while(que.size() > k){
            que.pop();
        }
    }
    std::vector<int>res;
    while(!que.empty()){
        res.push_back(que.top());
        que.pop();
    }
    for(int i = res.size() - 1; i >= 0; i--){
        std::cout << res[i] << " \n"[i == 0];
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  
  • 如果不要求选中的kk个元素有序,可以用std::nth_element()std::nth\_element()

  • 时间复杂度O(n)O(n)

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){   
    int n, k;
    std::cin >> n >> k;
    std::vector<int>a(n);
    for(int i = 0; i < n; i++){
        std::cin >> a[i];
    }
    std::nth_element(a.begin(), a.begin() + k, a.end(), std::greater<>());
    for(int i = 0; i < n; i++){
        std::cout << a[i] << " \n"[i == n - 1];
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11074 平面分割

看提示吧,好困难。

  • 时间复杂度O(1)O(1)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    i64 a = 1LL * n * (n + 1) / 2 + 1;
    i64 b = 1LL * n * n - n + 2;
    i64 c = 2LL * n * n - n + 1;
    i64 d = (9LL * n * n - 7 * n) / 2 + 1;
    std::cout << a << " " << b << " " << c << " " << d << "\n"; 
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11081 邮局选址

中位数定理,对横坐标和纵坐标分别选中位数即可

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    std::vector<int>x(n), y(n);
    for(int i = 0; i < n; i++){
        std::cin >> x[i] >> y[i];
    }
    std::sort(x.begin(), x.end());
    std::sort(y.begin(), y.end());
    int r = x[n / 2], c = y[n / 2];
    int ans = 0;
    for(int i = 0; i < n; i++){
        ans += std::abs(r - x[i]) + std::abs(c - y[i]);
    }
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11082 完全二叉树的种类(卡特兰数)

卡特兰数太难

可以看看oi-wiki.org/math/combin…

本题就是求卡特兰数

  • 时间复杂度O(n2)O(n^2)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    if(n <= 2){
        std::cout << "1\n";
        return;
    }else if(n == 3){
        std::cout << "2\n";
        return;
    }

    std::vector<int>f(n + 1);
    f[1] = f[2] = 1;
    f[3] = 2;

    auto dfs = [&](auto &&self, int x) -> int {
        if(f[x]){
            return f[x];
        }

        for(int i = 1; i < x; i++){
            f[x] += self(self, i) * self(self, x - i);
        }
        return f[x];
    };

    int ans = dfs(dfs, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11085 买票找零(卡特兰数)

f[cnt][x][y]f[cnt][x][y]表示有cntcnt5050元可以找零,还有xx人持5050元,yy人持100100元的方法数

然后跑记忆化搜索

初始化:x=0x=0时,若ycnty \le cnt,则f[cnt][x][y]=1f[cnt][x][y]=1

时间复杂度:O(n3)O(n^3)

#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

constexpr int N = 20;
int f[N][N][N];

void init(){
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            for(int k = 0; k < N; k++){
                f[i][j][k] = -1;
            }
        }
    }

    for(int i = 0; i < N; i++){
        for(int j = 0; j <= i; j++){
            f[i][0][j] = 1;
        }
    }
}
void solve(){
    init();

    int n;
    std::cin >> n;

    auto dfs = [&](auto &&self, int cnt, int x, int y) -> int {
        if(f[cnt][x][y] != -1){
            return f[cnt][x][y];
        }

        f[cnt][x][y] = 0;
        if(x){
            f[cnt][x][y] += self(self, cnt + 1, x - 1, y);
        }
        if(cnt && y){
            f[cnt][x][y] += self(self, cnt - 1, x, y - 1);
        }
        return f[cnt][x][y];
    };

    int ans = dfs(dfs, 0, n, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17083 多重幂计数问题

还是卡特兰数。

  • 时间复杂度O(n2)O(n^2)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    if(n <= 2){
        std::cout << "1\n";
        return;
    }else if(n == 3){
        std::cout << "2\n";
        return;
    }

    std::vector<int>f(n + 1);
    f[1] = f[2] = 1;
    f[3] = 2;

    auto dfs = [&](auto &&self, int x) -> int {
        if(f[x]){
            return f[x];
        }

        for(int i = 1; i < x; i++){
            f[x] += self(self, i) * self(self, x - i);
        }
        return f[x];
    };

    int ans = dfs(dfs, n);
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17965 幸运之星

(1)只需要维护一个起点和当前剩下的人之间相邻的距离,发现这个距离必然相同,每过一轮这个距离都会×2\times 2,最多递归loglog

(2)看提示,提示写的很详细了。注意应该用递推(dpdp)写而不是递归。

  • 时间复杂度O(n)O(n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n, m;
    std::cin >> n >> m;

    auto dfs = [&](auto &&self, int start, int len) -> int {
        if(start + len > n){
            return start;
        }

        return self(self, start + len, len * 2);
    };
    int ans1 = dfs(dfs, 1, 1);


    std::vector<int>dp(n + 1);
    dp[1] = 1;
    for(int i = 2; i <= n; i++){
        dp[i] = (dp[i - 1] + m - 1) % i + 1;
    }
    std::cout << ans1 << " " << dp[n] << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11086 排序问题再探讨

并非填空题,暴力sortsort即可

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    std::string s[] = {"Insert sort: ", "Natural merge sort: ", "Quick sort: "};
    int n;
    std::cin >> n;
    std::vector<int>a(n);
    for(int i = 0; i < 3; i++){
        for(int j = 0; j < n; j++){
            std::cin >> a[j];
        }
        std::sort(a.begin(), a.end());
        std::cout << s[i];
        for(int j = 0; j < n; j++){
            std::cout << a[j] << " \n"[j == n - 1];
        }
    }
}    

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11087 统计逆序对

树状数组的板子题

思路:

首先记录初始每个数的位置 然后对数组按值从大到小进行排序

然后 将排序后的数组中的数 按照原来的位置插入新数组

考虑如果当前插入的位置位inxinx 那么如果[inx+1,n][inx+1,n]这个位置内已经有数插入 说明形成了逆序对

然后求总和即可

由于设计到动态更新前缀和以及多次查询,朴素前缀和会直接超时,所以得用树状数组

树状数组的单点修和区间查复杂度都是loglog级别的

注意答案可能爆intint

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

template<typename T>
struct Fenwick{
    std::vector<T>a;
    int n;

    Fenwick(int n_ = 0){
        n = n_ + 1;
        init();
    }

    void init(){
        a.assign(n, T{});
    }

    void add(int pos, T k){
        for(int i = pos; i < n; i += i & -i){
            a[i] += k;
        }
    }

    T sum(int pos){
        T ans{};
        for(int i = pos; i; i -= i & -i){
            ans += a[i];
        }
        return ans;
    }

    T rangesum(int l, int r){
        if(!l){
            return sum(r);
        }
        else{
            return sum(r) - sum(l - 1);
        }
    }
};

struct dat{
    int val, id;
    dat(): val(-1), id(-1){};
};

void solve(){
    int n;
    std::cin >> n;
    std::vector<dat>a(n);
    for(int i = 0; i < n; i++){
        std::cin >> a[i].val;
        a[i].id = i + 1;
    }
    std::sort(a.begin(), a.end(), [&](const dat &x, const dat &y){
        if(x.val == y.val){
            return x.id < y.id;
        }
        return x.val < y.val;
    });

    Fenwick<int>f(n);
    i64 ans = 0;
    for(int i = 0; i < n; i++){
        int inx = a[i].id;
        ans += f.rangesum(inx, n);
        f.add(inx, 1);
    }
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11088 整数划分的扩展问题

(1)用所有m\le m的数做完全背包

(2)和问题(1)等价,具体证明可见提示,很妙

(3)用所有奇数做完全背包

(4)用所有nn以内的数做0101背包

  • 时间复杂度:
  • (1),(2):O(n×m)O(n \times m)
  • (3):O(n2)O(n^2)
  • (4):O(n2)O(n^2)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void get1(int n, int m){
    std::vector<int>a(m);
    for(int i = 0; i < m; i++){
        a[i] = i + 1;
    }
    std::vector<int>dp(n + 1);
    dp[0] = 1;
    for(int i = 0; i < m; i++){
        for(int j = a[i]; j <= n; j++){
            dp[j] += dp[j - a[i]];
        }
    }
    std::cout << dp[n] << " ";
}


void get3(int n){
    std::vector<int>a;
    for(int i = 1; i <= n; i += 2){
        a.push_back(i);
    }
    
    std::vector<int>dp(n + 1);
    dp[0] = 1;
    int siz = a.size();
    for(int i = 0; i < siz; i++){
        for(int j = a[i]; j <= n; j++){
            dp[j] += dp[j - a[i]];
        }
    }
    std::cout << dp[n] << " ";
}

void get4(int n){
    std::vector<int>a(n);
    for(int i = 0; i < n; i++){
        a[i] = i + 1;
    }
    std::vector<int>dp(n + 1);
    dp[0] = 1;
    for(int i = 0; i < n; i++){
        for(int j = n; j >= a[i]; j--){
            dp[j] += dp[j - a[i]];
        }
    }
    std::cout << dp[n] << "\n";
}

void solve(){
    int n, m;
    std::cin >> n >> m;
    get1(n, m);
    get1(n, m);
    get3(n);
    get4(n);
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17082 两个有序数列中找第kk

对两个序列一起跑双指针即可

  • 时间复杂度O(n+m)O(n+m),这主要来自输入两个序列的复杂度,找第kk小复杂度只有O(k)O(k)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n, m, k;
    std::cin >> n >> m >> k;
    std::vector<int>a(n), b(m);
    for(int i = 0; i < n; i++){
        std::cin >> a[i];
    }
    for(int i = 0; i < m; i++){
        std::cin >> b[i];
    }
    int inxa = 0, inxb = 0, cnt = 0;
    while(cnt < k - 1){
        if(inxa >= a.size()){
            cnt++;
            inxb++;
        }else if(inxb >= b.size()){
            cnt++;
            inxa++;
        }else{
            if(a[inxa] <= b[inxb]){
                inxa++;
            }else{
                inxb++;
            }
            cnt++;
        }
    }

    if(inxa >= a.size()){
        std::cout << b[inxb] << "\n";
    }else if(inxb >= b.size()){
        std::cout << a[inxa] << "\n";
    }else{
        std::cout << std::min(a[inxa], b[inxb]) << "\n";
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17087 输出所有组合

排序后跑深搜即可

  • 时间复杂度O(n2×2n)O(n^2 \times 2^n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::string s;
    std::cin >> n >> s;     
    std::sort(s.begin(), s.end());

    std::vector<std::string>res;
    std::string p;
    auto dfs = [&](auto &&self, int inx) -> void {
        if(!p.empty()){
            res.push_back(p);
        }

        for(int i = inx; i < n; i++){
            p += s[i];
            self(self, i + 1);
            p.pop_back();
        }
    };

    dfs(dfs, 0);
    std::sort(res.begin(), res.end(), [](const std::string &a, const std::string &b){
        if(a.size() == b.size()){
            return a < b;
        }
        return a.size() < b.size();
    });
    
    int siz = res.size();
    for(int i = 0; i < siz; i++){
        std::cout << i + 1 << " " << res[i] << "\n";
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/