点击打开链接
点击打开链接
计算两个时刻的时间间隔,减去 K K K 。
时间复杂度 O ( 1 ) O(1) O(1) 。
#include
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int get(int h, int m) {
return h * 60 + m;
}
int main() {
int h1, m1, h2, m2, k;
read(h1), read(m1), read(h2), read(m2), read(k);
cout << get(h2, m2) - get(h1, m1) - k << endl;
return 0;
}
不难发现对于任意字符串,将某个 P 替换成 D 不会使得答案变小。
因此,将所有问号替换成 D 即可。
时间复杂度 O ( ∣ T ∣ ) O(|T|) O(∣T∣) 。
#include
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
char s[MAXN];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
for (int i = 1; i <= n; i++)
if (s[i] == '?') s[i] = 'D';
printf("%s\n", s + 1);
return 0;
}
对于二叉树上有 x x x 个节点的一层,其上一层可能的非叶节点数为:
⌈ x 2 ⌉ , ⌈ x 2 ⌉ + 1 , ⌈ x 2 ⌉ + 2 , … , x \lceil\frac{x}{2}\rceil,\lceil\frac{x}{2}\rceil+1,\lceil\frac{x}{2}\rceil+2,\dots,x ⌈2x⌉,⌈2x⌉+1,⌈2x⌉+2,…,x
由此,我们自底向上可以求出每一层结点总数可能的区间 [ l i , r i ] [l_i,r_i] [li,ri] 。
问题有解,当且仅当 1 ∈ [ l 0 , r 0 ] 1\in[l_0,r_0] 1∈[l0,r0] 。
对于最大化节点总数的问题,我们只需要再自顶向下,进行一遍贪心即可。
时间复杂度 O ( N ) O(N) O(N) 。
#include
using namespace std;
const int MAXN = 1e5 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
ll a[MAXN], l[MAXN], r[MAXN];
int main() {
int n; read(n);
for (int i = 0; i <= n; i++)
read(a[i]);
if (n == 0) {
if (a[0] == 1) puts("1");
else puts("-1");
return 0;
}
for (int i = n; i >= 0; i--) {
l[i] = a[i] + (l[i + 1] + 1) / 2;
r[i] = a[i] + r[i + 1];
}
if (l[0] != 1) {
puts("-1");
return 0;
}
ll ans = 1, cur = 1;
for (int i = 1; i <= n; i++) {
cur -= a[i - 1];
cur = min(r[i], cur * 2);
ans += cur;
}
cout << ans << endl;
return 0;
}
每个点出度均不超过 1 1 1 的有向图形成了由一个基环内向树和树组成的森林。
出度为 0 0 0 的点必然是某一棵树的树根。
生成森林的边数 F F F 和图中环的总数 C C C 应当满足:
F = N − C F=N-C F=N−C
因此,不妨考虑对 C C C 的总和进行计数。
对于已经形成的环,显然可以简单地进行统计,其贡献即为 ( N − 1 ) k (N-1)^k (N−1)k 。
对于尚未形成的环,其一定是由若干个出度为 0 0 0 的点所在的树连接而成的。
考虑大小为 a 1 , a 2 , … , a n ( n ≥ 2 ) a_1,a_2,\dots,a_n\;(n\geq2) a1,a2,…,an(n≥2) 的根节点是出度为 0 0 0 的点的树,则在
( n − 1 ) ! × ( N − 1 ) k − n × ∏ i = 1 n a i (n-1)!\times (N-1)^{k-n}\times \prod_{i=1}^{n}a_i (n−1)!×(N−1)k−n×i=1∏nai
种情况下,这些树恰好连成了环。
特别地,对于 n = 1 n=1 n=1 的情况,情况总数应为
( N − 1 ) k − 1 × ( a 1 − 1 ) (N-1)^{k-1}\times (a_1-1) (N−1)k−1×(a1−1)
则设计动态规划加速枚举即可。
时间复杂度 O ( N 2 ) O(N^2) O(N2) 。
#include
using namespace std;
const int MAXN = 5e3 + 5;
const int P = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int n, p[MAXN], s[MAXN], f[MAXN];
int find(int x) {
if (f[x] == x) return x;
else return f[x] = find(f[x]);
}
int fac[MAXN], powk[MAXN], dp[MAXN];
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int power(int x, int y) {
if (y == 0) return 1;
int tmp = power(x, y / 2);
if (y % 2 == 0) return 1ll * tmp * tmp % P;
else return 1ll * tmp * tmp % P * x % P;
}
int main() {
int k = 0; read(n);
for (int i = 1; i <= n; i++)
f[i] = i, s[i] = 1;
for (int i = 1; i <= n; i++) {
read(p[i]);
k += p[i] == -1;
if (p[i] != -1) {
int x = find(i), y = find(p[i]);
if (x != y) {
f[x] = y;
s[y] += s[x];
}
}
}
dp[0] = powk[0] = fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % P;
powk[i] = 1ll * powk[i - 1] * (n - 1) % P;
}
int ans = 1ll * n * powk[k] % P;
for (int i = 1; i <= n; i++)
if (f[i] == i) {
if (p[i] == -1) {
for (int j = n; j >= 1; j--)
update(dp[j], 1ll * dp[j - 1] * s[i] % P);
} else update(ans, P - powk[k]);
}
for (int i = 1; i <= n; i++)
if (p[i] == -1) update(ans, P - 1ll * (s[i] - 1) * powk[k - 1] % P);
for (int i = 2; i <= k; i++)
update(ans, P - 1ll * dp[i] * fac[i - 1] % P * powk[k - i] % P);
cout << ans << endl;
return 0;
}
首先,将问题倒过来考虑,由字符串 T T T 开始,我们依次删去一个字符,直至 T T T 为空。
首要的观察是对于存在 0 0 0 的字符串,我们会优先删除 0 0 0 。这是因为对于一个删去 1 1 1 的操作,我们可以选择转而删去相邻的另一个字符,可以发现得到的结果不会变劣。
由此,对于连续的一段 1 1 1 ,我们关心的只是其个数的奇偶性,考虑删去相邻的两个 1 1 1 。
则 T T T 将会成为如下样式:
0 … 010 … 010 … 0 … 0\dots010\dots010\dots0\dots 0…010…010…0…
考虑此时每一个 1 1 1 对答案的贡献,记其之前的 0 0 0 的个数为 p p p ,之后的 0 0 0 的个数为 s s s 。
则对于奇数位的 1 1 1 ,删去所有 0 0 0 后其对答案的贡献有上界:
⌊ p 2 ⌋ + 1 + s \lfloor\frac{p}{2}\rfloor+1+s ⌊2p⌋+1+s
对于偶数位的 1 1 1 ,删去所有 0 0 0 后其对答案的贡献有上界:
⌊ p + 1 2 ⌋ + s \lfloor\frac{p+1}{2}\rfloor+s ⌊2p+1⌋+s
事实上,这个上界是可以取到的。删去第一段全部的 0 0 0 ,然后从左到右依次删去每一段的若干个零,直至每一段均剩余一个 0 0 0 ,最后从右往左删去所有的 0 0 0 即可取到该上界。
时间复杂度 O ( ∣ T ∣ ) O(|T|) O(∣T∣) 。
#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
char s[MAXN]; int t[MAXN], f[MAXN];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
int tot = 0, cnt[2] = {0, 0}; ll ans = 0;
for (int i = 1; i <= n; i++)
tot += s[i] == '0';
for (int i = 1; i <= n; i++)
if (s[i] == '0') cnt[0]++;
else {
if (s[i + 1] == '1') {
ans += tot + 1, i++;
cnt[1] += 2;
} else {
cnt[1] += 1;
ans += i % 2;
ans += tot - cnt[0];
if (i % 2) ans += cnt[0] / 2;
else ans += (cnt[0] + 1) / 2;
}
}
for (int i = 1; i <= cnt[1] - 1; i++)
ans += (i + 1) / 2;
cout << ans << endl;
return 0;
}
考虑如何判断某序列是否合法。在最终序列中,不应存在位置对 ( i , j ) ( i ≤ j ) (i,j)\;(i\leq j) (i,j)(i≤j) ,满足可以通过删去一些二进制位的方式使得 a i > a j a_i>a_j ai>aj ,且 a i a_i ai 和 a j a_j aj 在二进制上相差不止一位。
进一步考虑如何确定两个数 x , y x,y x,y 是否能够通过删去一些二进制位的方式满足条件。
从高到低考虑每一个数位,记当前数位为 c x , c y c_x,c_y cx,cy 。
( 1 ) (1) (1) 、若 c x = c y c_x=c_y cx=cy ,则显然这是一个不产生影响的数位。
( 2 ) (2) (2) 、若 c y = c x + 1 c_y=c_x+1 cy=cx+1 ,则表明若不删去当前数位,将有 x < y x
( 3 ) (3) (3) 、最后,若 c x = c y + 1 c_x=c_y+1 cx=cy+1 ,则表明已经有 y < x y
由此,考虑记 d p i , j dp_{i,j} dpi,j 表示 N = i , M = j N=i,M=j N=i,M=j 时的答案。
若最高位的序列不存在子串 10 10 10 ,即形如
00 … 011 … 1 00\dots011\dots1 00…011…1
则可以直接由 d p i − 1 , j dp_{i-1,j} dpi−1,j 转移得到。
否则,考虑最高位第一个出现的 1 1 1 和最后一个出现的 0 0 0 ,例如
00 … 01 x x … x 011 … 1 00\dots01xx\dots x011\dots1 00…01xx…x011…1
则 1 x x … x 0 1xx\dots x0 1xx…x0 中全部的元素的后续数位都应相等,记 x x x 的个数为 c c c ,则可以由 d p i − 1 , j − c − 1 dp_{i-1,j-c-1} dpi−1,j−c−1 转移得到。那么,枚举 x x x 的个数 k k k ,不难得到一个 O ( N × M 2 ) O(N\times M^2) O(N×M2) 的动态规划解法。
#include
using namespace std;
const int MAXN = 5005;
const int P = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int power(int x, int y) {
if (y == 0) return 1;
int tmp = power(x, y / 2);
if (y % 2 == 0) return 1ll * tmp * tmp % P;
else return 1ll * tmp * tmp % P * x % P;
}
int dp[MAXN][MAXN];
int main() {
int n, m; read(n), read(m);
for (int i = 1; i <= m; i++)
dp[0][i] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
update(dp[i][j], 1ll * dp[i - 1][j] * (j + 1) % P);
for (int k = 2; k <= j; k++)
update(dp[i][j], 1ll * dp[i - 1][j - k + 1] * (j - k + 1) % P * power(2, k - 2) % P);
}
cout << dp[n][m] << endl;
return 0;
}
观察转移,利用部分和简单优化即可。
时间复杂度 O ( N × M ) O(N\times M) O(N×M) 。
#include
using namespace std;
const int MAXN = 5005;
const int P = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int power(int x, int y) {
if (y == 0) return 1;
int tmp = power(x, y / 2);
if (y % 2 == 0) return 1ll * tmp * tmp % P;
else return 1ll * tmp * tmp % P * x % P;
}
int dp[MAXN][MAXN];
int main() {
int n, m; read(n), read(m);
for (int i = 1; i <= m; i++)
dp[0][i] = 1;
for (int i = 1; i <= n; i++) {
int sum = 0;
for (int j = 1; j <= m; j++) {
update(dp[i][j], 1ll * dp[i - 1][j] * (j + 1) % P);
update(dp[i][j], sum);
sum = (2ll * sum + 1ll * dp[i - 1][j] * j) % P;
}
}
cout << dp[n][m] << endl;
return 0;
}