有好久都没写博客呢了。。。
标题统计
签到题。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
char a;
int ans;
int main() {
// freopen("title.in", "r", stdin);
// freopen("title.out", "w", stdout);
while((scanf("%c", &a)) != EOF) {
if((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9')) ans ++;
}
printf("%d", ans);
return 0;
}
龙虎斗
模拟题,细节很多。先跑一遍循环算出龙方和虎方的气势,再暴力枚举要派去工兵的兵营,统计气势差最大值即可。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;
const int MAXN = 1e5 + 5;
const LL lox = 9223372036854775807;
int n, m, p, s, t, Num;
LL ans1, ans2, minn, Q, a[MAXN];
LL Abs_(LL x) {
return x > 0 ? x : -x;
}
int main() {
// freopen("fight.in", "r", stdin);
// freopen("fight.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%lld", &a[i]);
}
scanf("%d%d%d%d", &m, &p, &s, &t);
a[p] += s; minn = lox;
for(int i = 1; i <= n; i ++) {
if(i < m) {
ans1 += a[i] * (m - i);
}
else if(i > m) {
ans2 += a[i] * (i - m);
}
}
for(int i = 1; i <= n; i ++) {
if(i < m) {
Q = (LL)t * (m - i);
if((Abs_(ans1 + Q - ans2)) < minn) {
minn = Abs_(ans1 + Q - ans2);
Num = i;
}
}
else if(i > m) {
Q = (LL)t * (i - m);
if((Abs_(Q + ans2 - ans1)) < minn) {
minn = Abs_(Q + ans2 - ans1);
Num = i;
}
}
else {
if((Abs_(ans1 - ans2)) < minn) {
minn = Abs_(ans1 - ans2);
Num = i;
}
}
}
printf("%d", Num);
return 0;
}
摆渡车
d
p
dp
dp题。
这道题还挺有难度的。
初看题以为是贪心,仔细想了一会儿后发现贪心做不了,于是改成了
d
p
dp
dp,然后,,,就WA了。
我认为做这种题不能光看表面,要挖掘题的模型。就比如这道题,题意就可以翻译成:求将时间轴分成任意个区间,使各个区间长度不小于
m
m
m,各个点到它属于区间的右端点距离的和的最小值。
令
d
p
[
i
]
dp[i]
dp[i]等于上面那一堆东西。
于是就不难推出
d
p
[
i
]
=
min
j
≤
i
−
m
{
d
p
(
j
)
+
∑
j
<
a
[
k
]
≤
i
{
i
−
a
[
k
]
}
}
dp[i]=\min \limits _ { j \leq { i - m} } \{dp(j) + \sum \limits _ {j<a[k]\leq i} \{i-a[k]\}\}
dp[i]=j≤i−mmin{dp(j)+j<a[k]≤i∑{i−a[k]}}又因为
∑
{
i
−
a
[
k
]
}
=
∑
{
i
}
−
∑
{
a
[
k
]
}
\sum\{i - a[k]\}=\sum\{i\}-\sum\{a[k]\}
∑{i−a[k]}=∑{i}−∑{a[k]}
即满足
j
<
a
[
k
]
≤
i
j < a[k] \leq i
j<a[k]≤i条件的
k
k
k的个数
×
i
−
\times i\ -
×i −这
k
k
k个点的位置和。
于是令
p
r
e
[
i
]
=
(
−
∞
,
i
]
pre[i]=(-\infty, i]
pre[i]=(−∞,i]中 点的位置和,
c
n
t
[
i
]
=
(
−
∞
,
i
]
cnt[i]=(-\infty, i]
cnt[i]=(−∞,i]中的点数。
则
d
p
[
i
]
=
min
j
≤
i
−
m
{
d
p
(
j
)
−
(
p
r
e
[
i
]
−
p
r
e
[
j
]
)
+
(
c
n
t
[
i
]
−
c
n
t
[
j
]
)
×
i
}
dp[i]=\min \limits _ { j \leq { i - m} } \{dp(j) - (pre[i] - pre[j]) + (cnt[i] - cnt[j])\times i\}
dp[i]=j≤i−mmin{dp(j)−(pre[i]−pre[j])+(cnt[i]−cnt[j])×i}
相信读者很容易就能想到,最后的答案
a
n
s
=
max
m
a
x
x
≤
i
≤
m
a
x
x
+
m
−
1
{
d
p
[
i
]
}
ans = \max \limits_{maxx \leq i \leq {maxx + m - 1}}\{dp[i]\}
ans=maxx≤i≤maxx+m−1max{dp[i]}
其中
m
a
x
x
maxx
maxx为所有
a
[
i
]
a[i]
a[i]的最大值。
此时时间复杂度
O
(
T
2
)
O(T^2)
O(T2)。
这样交上去是拿不了满分的。于是我们考虑优化:
1.将状态转移方程改为
d
p
[
i
]
=
min
i
−
2
×
m
≤
j
≤
i
−
m
{
d
p
(
j
)
−
(
p
r
e
[
i
]
−
p
r
e
[
j
]
)
+
(
c
n
t
[
i
]
−
c
n
t
[
j
]
)
×
i
}
dp[i]=\min \limits _ {{i - 2 \times m}\leq j \leq { i - m} } \{dp(j) - (pre[i] - pre[j]) + (cnt[i] - cnt[j])\times i\}
dp[i]=i−2×m≤j≤i−mmin{dp(j)−(pre[i]−pre[j])+(cnt[i]−cnt[j])×i}
为什么呢?你想,当区间长度大于了
2
×
m
2\times m
2×m,可以把这个大区间分成很多个小区间,答案一定不会比原来更差,所以此优化正确。
2.若
c
n
t
[
i
]
=
c
n
t
[
i
−
m
]
cnt[i]=cnt[i-m]
cnt[i]=cnt[i−m],即此长度为
m
m
m的区间中没有一个点,那
d
p
[
i
]
=
d
p
[
i
−
m
]
dp[i]=dp[i-m]
dp[i]=dp[i−m],就不用去跑第二重循环了。
由于优化2使进入第二重循环的次数不超过
m
×
n
m\times n
m×n次,优化1使第二重循环的时间复杂度降为了
m
m
m。所以总时间复杂度:
O
(
n
m
2
+
T
)
O(n m^2+T)
O(nm2+T)。
听说还有更厉害的优化方法,但是我不会…
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 505, MAXX = 4 * 1e6 + 110, inx = 2147483647;
int n, m, a[MAXN], maxx = -inx, C[MAXX], dp[MAXX], pre[MAXX], cnt[MAXX], Min = inx;
int main() {
// freopen("bus.in", "r", stdin);
// freopen("bus.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
pre[a[i]] += a[i]; cnt[a[i]] ++;
maxx = max(maxx, a[i]);
}
for(int i = 1; i < maxx + m; i ++) {
pre[i] += pre[i - 1];
cnt[i] += cnt[i - 1];
}
for(int i = 0; i < maxx + m; i ++) {
if(i >= m && cnt[i] == cnt[i - m]) {
dp[i] = dp[i - m];
continue;
}
dp[i] = cnt[i] * i - pre[i];
for(int j = max(i - 2 * m + 1, 0); j <= i - m; j ++) {
dp[i] = min(dp[i], dp[j] - (pre[i] - pre[j]) + (cnt[i] - cnt[j]) * i);
}
}
for(int i = maxx; i < maxx + m; i ++) {
Min = min(Min, dp[i]);
}
printf("%d", Min);
return 0;
}
对称二叉树
暴力或树Hash
作为T4,竟然能被暴力水过。。。
法一:暴力枚举
对的,你没看错,就是暴力。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define uLL unsigned long long
using namespace std;
const int MAXN = 1e6 + 5;
bool vis[MAXN];
int a[MAXN], n, zs[MAXN], maxx = 1;
struct Node {
int L_C, R_C;
}arr[MAXN];
int dfs(int Num) {
if(zs[Num]) return zs[Num];
if(arr[Num].L_C != -1) zs[Num] += dfs(arr[Num].L_C);
if(arr[Num].R_C != -1) zs[Num] += dfs(arr[Num].R_C);
zs[Num] ++;
return zs[Num];
}
bool check(int x, int y) {
if(x == -1 && y == -1) return 1;
if(x != -1 && y != -1 && a[x] == a[y] && check(arr[x].L_C, arr[y].R_C) && check(arr[x].R_C, arr[y].L_C)) return 1;
return 0;
}
int main() {
// freopen("tree.in", "r", stdin);
// freopen("tree.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i ++) {
scanf("%d%d", &arr[i].L_C, &arr[i].R_C);
}
zs[1] = dfs(1);
for(int i = 1; i <= n; i ++) {
if(check(arr[i].L_C, arr[i].R_C)) {
maxx = max(maxx, zs[i]);
}
}
printf("%d", maxx);
return 0;
}
此算法在树为完全二叉树时运行最慢(因为其他情况可以剪掉很多枝),时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)。
法二:树Hash
先咕着,明天更,,,