贡献转移
在计算每个元素的作用的时候,我们可以通过反向枚举作用效果,添加到作用元素的身上,这种方法叫做贡献转移。更正式的说,设 a i a_i ai为每个元素的作用数,集合 B B B为作用效果,那么:
a i = ∑ b ∈ B , b → a i 1 a_i = \sum_{b \in B,b \to a_i} 1 ai=b∈B,b→ai∑1
例题
考虑每一个位置的 pivot ,如果 i i i位置能够成为 pivot 的话,那么前后肯定存在一个位置 j j j,将位置 j j j变成 k k k之后,该位置的 pivot 成立,那么我们称 j j j位置对 i i i位置有一个贡献,此时我们可以用哈希表去记录 j j j位置的数字,然后在遍历中进行贡献转移即可。
class Solution
{
public:
int waysToPartition(vector<int> &nums, int k)
{
long long lsum = 0;
long long rsum = accumulate(nums.begin(), nums.end(), 0ll);
unordered_map<long long, int> mp;
vector<int> contrib(nums.size());
int undo = 0;
for (int i = 0; i < nums.size(); i++)
{
contrib[i] += mp[nums[i]];
if (i != nums.size() - 1)
{
lsum += nums[i];
rsum -= nums[i];
if (rsum == lsum)
{
undo++;
}
mp[rsum - lsum + k]++;
}
}
mp.clear();
lsum = accumulate(nums.begin(), nums.end(), 0ll);
rsum = 0;
for (int i = nums.size() - 1; i >= 0; i--)
{
contrib[i] += mp[nums[i]];
if (i != 0)
{
lsum -= nums[i];
rsum += nums[i];
mp[lsum - rsum + k]++;
}
}
for (int i = 0; i < nums.size(); i++)
{
undo = max(undo, contrib[i]);
}
return undo;
}
};
我们可以枚举以 x x x结尾,前驱为 a i + 1 a_i + 1 ai+1的 ⌈ a i a i + 1 ⌉ − 1 \lceil \frac{a_i}{a_i + 1} \rceil - 1 ⌈ai+1ai⌉−1贡献。设 d p [ i ] [ x ] dp[i][x] dp[i][x]表示为子数组 a [ i : j ] a[i:j] a[i:j]最终以 x x x结尾的子数组的数量,那么前面有 i i i个开头的子数组会得到这个贡献。即为 i ∗ d p [ i ] [ x ] ∗ ( ⌈ a i x ⌉ − 1 ) i * dp[i][x] * (\lceil \frac{a_i}{x} \rceil - 1) i∗dp[i][x]∗(⌈xai⌉−1)。
int arr[100005];
int dp[2][100005];
vector<int> hold[2];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", arr + i);
ll ans = 0;
hold[0].clear();
hold[1].clear();
for (int i = n; i >= 1; i--)
{
dp[i & 1][arr[i]]++;
hold[i & 1].push_back(arr[i]);
int las = arr[i];
for (int x : hold[(i + 1) & 1])
{
int pos = arr[i] / ((arr[i] + x - 1) / x);
ll cnt = dp[(i + 1) & 1][x];
dp[i & 1][pos] += cnt;
if (pos != las)
{
hold[i & 1].push_back(pos);
las = pos;
}
ans = (ans + (i * ((cnt * (((arr[i] + x - 1) / x) - 1)) % MOD353)) % MOD353) % MOD353;
}
for (int k : hold[(i + 1) & 1])
dp[(i + 1) & 1][k] = 0;
hold[(i + 1) & 1].clear();
}
for (int k : hold[0])
dp[0][k] = 0;
for (int k : hold[1])
dp[1][k] = 0;
cout << ans << endl;
}