T1:舞会配对

本题难度中等,注意到数据范围很小,正解极有可能是朴素的搜索枚举方法。

\(m = 2n\)

使用回溯法,依次考虑第 \(1 \sim m\) 个人,要与谁配对
记录状态:

  • 当前考虑配对的人的编号
  • 已经配成的对数
  • 当前(未完全的)配对方案的幸福度
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n;
    cin >> n;
    
    int m = 2*n;
    vector<vector<int>> a(m, vector<int>(m));
    rep(i, m)rep(j, m) cin >> a[i][j];
	
	ll ans = -1e18;
	vector<int> matched(m);
	auto dfs = [&](auto& f, int step, int cnt, ll now) -> void {
    	if (cnt == n) {
    	    ans = max(ans, now);
    	    return;
    	}
    	
    	if (matched[step]) {
    	    f(f, step+1, cnt, now);
    	    return;
    	} 
    	for (int i = step+1; i < m; ++i) {
    	    // 1. 剪枝
    	    if (matched[i]) continue;
    	    // 2. 试探
    	    matched[i] = matched[step] = true;
    	    f(f, step+1, cnt+1, now*a[step][i]);
    	    // 3. 回溯
    	    matched[i] = matched[step] = false;
    	}
	};
	
	dfs(dfs, 0, 0, 1);
	
	cout << ans << '\n';
    
    return 0;
}

如何分析时间复杂度?

\(m = 16\) 的极端情形:

对第 \(1\) 个待配对的人,后续有 \(15\) 个可选的与其配对的人
对第 \(2\) 个待配对的人,后续有 \(13\) 个可选的与其配对的人
对第 \(3\) 个待配对的人,后续有 \(11\) 个可选的与其配对的人

\(~\vdots\)

对第 \(8\) 个待配对的人,后续有 \(1\) 个可选的与其配对的人

由乘法原理,需要枚举的所有配对方案为:

\[15!! = 15 \times 13 \times \cdots \times 3 \times 1 = 2027025 \]

关于本题的背景:

本题为边权之积,可以通过取对数,化积为和。
实质为 二分图最大权匹配问题
时间复杂度为 \(O(n^3)\)
适用范围:提高组以上

T2:发积分

本题难度较大,显然 \(x\) 越大 \(k\) 天内能发的分数越多。二分 \(x\)\(check(x)\) 计算发 \(k\) 天实际能发多少积分,判断是否能发出 \(n\) 分。找到能发完 \(n\) 分的最小 \(x\)

本题 \(k\) 比较小,\(check\) 计算时,直接模拟 \(k\) 天发积分即可。

使用整除分块技巧,一次可达 \(O(\sqrt{k})\),所以 \(k\) 的范围可以强化到 \(10^{12}\)

显然,\(x=n\) 时,可以满足要求:

\[\lceil\frac{x}{1}\rceil + \lceil\frac{x}{2}\rceil + \cdots + \lceil\frac{x}{k}\rceil \geqslant n \]

答案要求 \(x_{\min}\),而 \([x_{\min}, n]\) 中的任何值都满足要求。

考虑到可能的答案分成了两段,可以考虑使用二分答案法。
二分答案 \(=\) 二分框架 \(+\) 判定函数。

判断:对于当前的二分值 \(x\),是否满足:

\[\lceil\frac{x}{1}\rceil + \lceil\frac{x}{2}\rceil + \cdots + \lceil\frac{x}{k}\rceil \geqslant n \]

每次判断时间复杂度 \(O(k)\),二分范围为 \([1, n]\)
总时间复杂度 \(O(k\log n)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    ll n; int k;
    cin >> n >> k;
    
    ll wa = 0, ac = n;
    while (ac-wa > 1) {
        ll wj = (ac+wa)/2;
        
        auto ok = [&]{
            ll res = 0;
            for (int i = 1; i <= k; ++i) {
                res += (wj+i-1)/i;
            }
            return res >= n;
        }();
        
        (ok ? ac : wa) = wj;
    }
    
    cout << ac << '\n';
    
    return 0;
}