lc2021
差分+sort
事件处理: [start, end+1) ,对应事件为 (start, +1) 和 (end+1, -1) ,排序后遍历事件计算每个位置的亮度
遍历统计:依次用前一个数 累加亮度变化值,即为变化点
实时更新最大亮度和对应位置,保证找到最亮且坐标最小的位置
class Solution {
public:
int brightestPosition(vector<vector<int>>& lights) {
vector<pair<int, int>> events;
for (auto& light : lights) {
int start = light[0] - light[1];
int end = light[0] + light[1] + 1; // 转换为左闭右开区间,方便差分
events.emplace_back(start, 1);
events.emplace_back(end, -1);
}
sort(events.begin(), events.end());
int maxBright = 0, currentBright = 0, ans = 0;
for (auto& [pos, delta] : events) {
currentBright += delta;
if (currentBright > maxBright) {
maxBright = currentBright;
ans = pos;
}
}
return ans;
}
};
emplace_back 适合需要直接在容器内构造对象的场景(可避免临时对象拷贝/移动,提升性能)
push_back 适合传递已有对象(或临时对象会被隐式构造时),逻辑更直观
sum++:需要"原地构造"选 emplace_back ,传递"现成对象"选 push_back++
lc3317
"拆分问题→分别计算各部分→相乘求和"
const int mod = 1e9 + 7;
const int MX = 1001;
long long s[MX][MX];
int init = []()->int {
//i个人分成j组
s[0][0] = 1;
for (int i = 1; i < MX; i++)
{
for (int j = 1; j < MX; j++)
{
s[i][j] = (s[i - 1][j - 1] + (long long)j*s[i - 1][j])%mod;
}
}
return 0;
}();
class Solution {
public:
int numberOfWays(int n, int x, int y) {
long long powY = 1;
long long A = 1;
long long ans = 0;
for (int i = 1; i <= min(n, x); i++)
{
A = (A*(x - i + 1)) % mod;
powY = powY * y%mod;
s[n][i]%=mod;
ans = (ans + ((A * powY)%mod*s[n][i])%mod)%mod;
}
return ans;
}
};
一、问题在问什么
计算**"把 n 个人安排到 x 个节目里,每个节目至少1人,且每个节目有 y 种打分方式"的总方案数**
二、概念:第二类斯特林数
定义: S(n, i) 表示把 n 个不同的元素分成 i 个非空集合(集合无顺序)的方案数。
比如 S(3, 2) 就是把3个人分成2个非空组的方式数,结果是3(比如{1,2}和{3}、{1,3}和{2}、{2,3}和{1})
三、递推公式怎么来
++S(n, i) = S(n-1, i-1) + i * S(n-1, i)++
-
第一种情况:第 n 个人自己 ++单独成一组++ → 剩下 n-1 个人分成 i-1 组,即 S(n-1, i-1) 。
-
第二种情况:第 n 个人 ++加入之前++的 i 组中的某一组 → 有 i 种选择,剩下 n-1 个人分成 i 组,即 i * S(n-1, i) 。
四、代码
- 预处理斯特林数( init 函数)
s[0][0] = 1; // 0个人分成0组,算1种方案
for (int i = 1; i < MX; i++)
for (int j = 1; j < MX; j++)
++s[i][j] = (s[i-1][j-1] + (long long)j * s[i-1][j]) % mod;++
这部分是提前计算所有可能的 S(n, i) ,存在 s 数组里,方便后面直接用
- 计算最终答案( numberOfWays 函数)
我们要枚举"选 i 个节目来安排人",然后把三部分方案数相乘:
- A :从 x 个节目中选 i 个有顺序的方案数(排列数 A(x, i) = x*(x-1)*...*(x-i+1) )
++- S(n, i) :把 n 个人分成 i 个非空组的方案数(用预处理好的 s 数组)++
- powY :每个节目有 y 种打分方式, i 个节目就是 y^i 种
最后把所有 i 的情况加起来,就是最终答案。
int numberOfWays(int n, int x, int y) {
long long powY = 1;
long long A = 1;
long long ans = 0;
for (int i = 1; i <= min(n, x); i++) {
A *= (x - i + 1);
powY *= y;
ans += ++A * powY * s[n][i];++
}
return ans;
}
五、举个例子
比如 n=3 , x=2 , y=2 :
-
当 i=1 :选1个节目,把3人分成1组,打分方式 2^1 → 方案数 A(2,1)*S(3,1)*2^1 = 2*1*2 = 4 。
-
当 i=2 :选2个节目,把3人分成2组,打分方式 2^2 → 方案数 A(2,2)*S(3,2)*4 = 2*3*4 = 24 。
-
总答案: 4+24=28
lc600
数位dp

class Solution {
public:
int findIntegers(int n) {
vector<int> digits;
int x = n;
while (x > 0) {
digits.push_back(x % 2);
x /= 2;
}
reverse(digits.begin(), digits.end());
vector<vector<int>> memo(digits.size(), vector<int>(2, -1));
function<int(int, int, bool)> dfs = [&](int pos, int pre, bool isLimit) -> int {
++if (pos == digits.size())
return 1;++
if (!isLimit && memo[pos][pre] != -1) {
return memo[pos][pre];
}
int up = isLimit ? digits[pos] : 1;
int res = 0;
for (int d = 0; d <= up; d++) {
++bool newLimit = isLimit && (d == up);++
if (pre == 1 && d == 1) {
continue;
}
++res += dfs(pos + 1, d, newLimit);++
}
if (!isLimit) {
memo[pos][pre] = res;
}
return res;
};
return dfs(0, 0, true);
}
};

- 条件变了:比如"数位和是偶数",就把 pre 改成"前几位的数位和的奇偶性",然后判断最终和是否为偶。
- 进制变了:比如十进制题目,就把拆数逻辑改成 %10 和 /10 , up 改成9即可。