这场数数
好数(number)100pts
找三个数的和,而且允许 $ \Theta(n^2) $,那么我们可以维护出两个数的和,然后每次顺序遍历找这个数减去前面的某个数在任意两个数的和中有没有出现过,这个也是 $ \Theta(n^2) $ 的;
所以时间复杂度:$ \Theta(n^2) $,如果带 $ \log $ 会过不去,要用桶维护;
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int a[500005];
int ans;
bool vis[5000005][2];
int main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
if (a[1] * 2 < 0) vis[-1 * a[1] * 2][0] = true;
else vis[a[1] * 2][1] = true;
for (int i = 2; i <= n; i++) {
bool vi = false;
for (int j = 1; j < i; j++) {
int now = a[i] - a[j];
if (now < 0) {
if (vis[-1 * now][0]) {
vi = true;
break;
}
} else {
if (vis[now][1]) {
vi = true;
break;
}
}
}
if (vi) ans++;
for (int j = 1; j <= i; j++) {
int now = a[i] + a[j];
if (now < 0) vis[-1 * now][0] = true;
else vis[now][1] = true;
}
}
cout << ans;
return 0;
}
SOS字符串(sos)0pts
赛时容斥了3.5h不对,其实不能容斥,因为找不到正好大于等于某个数的,总是会有重复;
所以考虑DP(貌似除了容斥就是DP把);
设 $ f_{i, j, 0/1/2} $ 表示考虑前 $ i $ 位,已经匹配了 $ j $ 个 SOS
,现在匹配到了 SOS
的第一位(1),第二位(2),第三位或者其它字母(0)时的方案数;
转移时正常转移,但是是 $ \Theta(n^2) $ 的;
当我们写出转移方程时,其实会发现对于 $ j \geq 2 $ 的状态,转移都是重复的,所以我们不需要挨个转移,直接统计一下即可;
时间复杂度: $ \Theta(n) $,有常数;
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const long long mod = 1e9 + 7;
long long n;
long long f[1000005][5][5];
int main() {
freopen("sos.in", "r", stdin);
freopen("sos.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
if (n < 9) {
cout << 0;
return 0;
}
if (n == 9) {
cout << 1;
return 0;
}
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 2; j++) {
f[i][j][0] = (f[i - 1][j][0] * 25 % mod + f[i - 1][j][1] * 24 % mod + f[i - 1][j][2] * 25 % mod) % mod;
if (j) f[i][j][0] = (f[i][j][0] + f[i - 1][j - 1][2]) % mod;
f[i][j][1] = (f[i - 1][j][0] + f[i - 1][j][1]) % mod;
f[i][j][2] = f[i - 1][j][1];
}
f[i][3][0] = (f[i - 1][3][0] * 26 % mod + f[i - 1][2][2] % mod) % mod;
}
cout << f[n][3][0];
return 0;
}
集训营的气球(balloon)0pts
首先考虑没有修改怎么做,那么我们发现这是一个背包问题,设 $ f_{i, j} $ 表示前 $ i $ 个人,有 $ j $ 个人买了特殊气球的方案数,转移考虑第 $ i $ 个人买不买特殊气球即可;
可以滚动数组;
但是有修改怎么办?
暴力的思路是每次修改重新跑一遍背包,这样时间复杂度显然不对;
那么我们怎么干?有个东西叫做退背包;
就是做背包的时候反着做,退背包的时候正着做,然后把运算取反即可;
对于正确性,可以参考Luogu P4141;
所以这里就是先强制撤销这个数,然后在加进来;
最后用一个小容斥,用总方案数减去小于c的方案数;
所以时间复杂度;$ \Theta(nc \log mod) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long mod = 1e9 + 7;
long long ksm(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int n, c, q;
int a[1000005], b[1000005];
long long f[1000005], tot;
void w() {
f[0] = 1;
tot = 1;
for (int i = 1; i <= n; i++) {
for (int j = c - 1; j >= 1; j--) {
f[j] = (f[j - 1] * a[i] % mod + f[j] * b[i] % mod) % mod;
}
f[0] = f[0] * b[i] % mod;
tot = (tot * (a[i] + b[i]) % mod) % mod;
}
}
int main() {
freopen("balloon.in", "r", stdin);
freopen("balloon.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> c;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
cin >> q;
w();
int s, x, y;
for (int i = 1; i <= q; i++) {
cin >> s >> x >> y;
tot = tot * ksm(((a[s] + b[s]) % mod), mod - 2) % mod * ((x + y) % mod) % mod;
long long inv = ksm(b[s], mod - 2);
f[0] = f[0] * inv % mod;
for (int j = 1; j < c; j++) f[j] = (f[j] - f[j - 1] * a[s] % mod + mod) % mod * inv % mod;
long long ans = 0;
for (int j = c - 1; j >= 1; j--) {
f[j] = (f[j - 1] * x % mod + f[j] * y % mod) % mod;
ans = (ans + f[j]) % mod;
}
f[0] = f[0] * y % mod;
ans = (ans + f[0]) % mod;
a[s] = x;
b[s] = y;
cout << (tot - ans + mod) % mod << '\n';
}
return 0;
}