1.买铅笔
算法分析
不能整除则加1。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
int main()
{
int n; scanf("%d", &n);
int snum, sprice;
int ans = 1e8, t;
for (int i = 1; i <= 3; ++i)
{
scanf("%d%d", &snum, &sprice);
t = n / snum * sprice + (n % snum == 0 ? 0 : 1) * sprice;
ans = min(ans, t);
}
printf("%d\n", ans);
}
2.回文日期
算法分析
直接枚举每一个日期然后判断是否回文,肯定超时。注意到,对于固定年份,只能有一个回文日期,比如2142年,其生成的回文日期为21422412,然后判断这个日期是否合法即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
int v[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int shuiwen(int syear) // 1201
{
return syear * 10000 + syear % 10 * 1000 + syear / 10 % 10 * 100 + syear / 100 % 10 * 10 + syear / 1000;
}
int srun(int syear)
{
if ((syear % 4 == 0 && syear % 100 != 0) || syear % 400 == 0) return 1;
else return 0;
}
int sjudge(int s)
{
int syue = s / 1000 % 10 * 10 + s / 100 % 10;
int sday = s % 100;
if (syue < 1 || syue > 12) return 0;
if (srun(s / 10000)) v[2] = 29; else v[2] = 28;
if (sday < 1 || sday > v[syue]) return 0;
return 1;
}
int main()
{
int sd1, sd2, snum = 0;
scanf("%d%d", &sd1, &sd2);
for (int k = sd1 / 10000; k <= sd2 / 10000; ++k)
{
int sdata = shuiwen(k); // 生成回文日期
if (sjudge(sdata) && sdata >= sd1 && sdata <= sd2) ++snum;
}
printf("%d\n", snum);
return 0;
}
算法拓展
枚举后面四位,即枚举月份+天数,然后生成年份,效率更高。最多有366种。然后判断生成的年份是否在范围之内即可。
3.海港
算法分析
单调队列维护每艘船的情况。 v i s vis vis数组标记国籍。对于新到来的船只,如果有国籍 x x x,则 + + v i s [ x ] ++vis[x] ++vis[x],如果此时 v i s [ x ] vis[x] vis[x]为1,则答案加1。对于队首的船只,如果要出队,如果有国籍 x x x,则 − − v i s [ x ] --vis[x] −−vis[x],如果此时 v i s [ x ] vis[x] vis[x]为0,则答案减1。
用 v e c t o r vector vector存每艘船的人员信息。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
using namespace std;
int n, vis[100010];
int q[100010], l, r;
struct node
{
int t, k;
vector<int> s;
}sboot[100010];
int main()
{
int n; scanf("%d", &n);
int num = 0, x;
l = 1, r = 0;
for (int i = 1; i <= n; ++i)
{
scanf("%d%d", &sboot[i].t, &sboot[i].k);
for (int j = 1; j <= sboot[i].k; ++j)
{
scanf("%d", &x); sboot[i].s.push_back(x);
if (vis[x] == 0) ++num;
++vis[x];
}
q[++r] = i;
while (l <= r && sboot[q[l]].t <= sboot[i].t - 86400)
{
for (int j = 0; j < sboot[q[l]].s.size(); ++j)
{
x = sboot[q[l]].s[j];
--vis[x];
if (vis[x] == 0) --num;
}
++l;
}
printf("%d\n", num);
}
return 0;
}
4.魔法阵
算法分析
根据题目给定的关系,设 x c xc xc到 x d xd xd之间的距离为 t t t,则 x b xb xb到 x c xc xc之间的距离为 6 t + k 6t+k 6t+k, k k k最小值为1。 x a xa xa到 x b xb xb之间的距离为 2 t 2t 2t。魔法的取值范围是 [ 1 , n ] [1, n] [1,n],可以用 v i s vis vis数组标记某个魔法值是否被取到,以及被取了几次。设:
c
n
t
[
x
]
[
0
]
cnt[x][0]
cnt[x][0]表示:魔法值为
x
x
x的魔法物品作为A出现的次数。
c
n
t
[
x
]
[
1
]
cnt[x][1]
cnt[x][1]表示:魔法值为
x
x
x的魔法物品作为B出现的次数。
c
n
t
[
x
]
[
2
]
cnt[x][2]
cnt[x][2]表示:魔法值为
x
x
x的魔法物品作为C出现的次数。
c
n
t
[
x
]
[
3
]
cnt[x][3]
cnt[x][3]表示:魔法值为
x
x
x的魔法物品作为D出现的次数。
以上分析中有三个未知量: a a a的位置设为 x x x, t t t和 k k k。枚举这三个未知量,不用具体考虑每个魔法取值是否有物品。比如:
c
n
t
[
a
x
]
[
0
]
+
=
v
i
s
[
b
x
]
∗
v
i
s
[
c
x
]
∗
v
i
s
[
d
x
]
cnt[ax][0] += vis[bx] * vis[cx] * vis[dx]
cnt[ax][0]+=vis[bx]∗vis[cx]∗vis[dx]
以上是计算魔法值
a
x
ax
ax作为A物品出现的次数。假如
b
x
bx
bx没有对应物品,即:
v
i
s
[
b
x
]
=
0
vis[bx] = 0
vis[bx]=0,则最后
v
i
s
[
b
x
]
∗
v
i
s
[
c
x
]
∗
v
i
s
[
d
x
]
vis[bx] * vis[cx] * vis[dx]
vis[bx]∗vis[cx]∗vis[dx]为0,对
c
n
t
[
a
x
]
[
0
]
cnt[ax][0]
cnt[ax][0]没有贡献。能过约85%的数据。可考虑优化。
假设 a x 、 b x 、 c x 、 d x ax、bx、cx、dx ax、bx、cx、dx这四个值构成魔法阵,则 a x 、 b x 、 c x + 1 、 d x + 1 ax、bx、cx+1、dx+1 ax、bx、cx+1、dx+1这四个值也能构成魔法阵, a x 、 b x 、 c x + 2 、 d x + 2 ax、bx、cx+2、dx+2 ax、bx、cx+2、dx+2也能构成魔法阵,……
计算 c n t [ a x ] [ 0 ] cnt[ax][0] cnt[ax][0]:
c
n
t
[
a
x
]
[
0
]
+
=
v
i
s
[
b
x
]
∗
v
i
s
[
c
x
]
∗
v
i
s
[
d
x
]
cnt[ax][0] += vis[bx] * vis[cx] * vis[dx]
cnt[ax][0]+=vis[bx]∗vis[cx]∗vis[dx]
c
n
t
[
a
x
]
[
0
]
+
=
v
i
s
[
b
x
]
∗
v
i
s
[
c
x
+
1
]
∗
v
i
s
[
d
x
+
1
]
cnt[ax][0] += vis[bx] * vis[cx+1] * vis[dx+1]
cnt[ax][0]+=vis[bx]∗vis[cx+1]∗vis[dx+1]
c
n
t
[
a
x
]
[
0
]
+
=
v
i
s
[
b
x
]
∗
v
i
s
[
c
x
+
2
]
∗
v
i
s
[
d
x
+
2
]
cnt[ax][0] += vis[bx] * vis[cx+2] * vis[dx+2]
cnt[ax][0]+=vis[bx]∗vis[cx+2]∗vis[dx+2]
……
……
对于以上的计算,我们可以用类似于前缀和的后缀和优化,维护 v i s [ c x ] ∗ v i s [ d x ] vis[cx]*vis[dx] vis[cx]∗vis[dx]的后缀和。
然后逆序枚举 d x dx dx和 c x cx cx,维护 v i s [ a x ] ∗ v i s [ b x ] vis[ax]*vis[bx] vis[ax]∗vis[bx]的前缀和。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
using namespace std;
int n, m, vis[15010], a[40010];
int cnt[15010][4];
int sum[15010];
int main()
{
scanf("%d%d", &n, &m);
int x;
for (int i = 1; i <= m; ++i)
{
scanf("%d", &x);
a[i] = x;
++vis[x];
}
// 正序枚举a和b
for (int t = 1; t <= (n - 1) / 9; ++t)
{
for (int k = n; k > n - t; --k) sum[k] = 0;
for (int k = n - t; k >= 1; --k) sum[k] = vis[k] * vis[k+t] + sum[k+1];
for (int a = 1; a + 9 * t + 1 <= n; ++a)
{
cnt[a][0] += vis[a + 2 * t] * sum[a + 8 * t + 1];
cnt[a + 2 * t][1] += vis[a] * sum[a + 8 * t + 1];
}
}
// 倒序枚举d和c
for (int t = 1; t <= (n - 1) / 9; ++t)
{
for (int k = 1; k < 2 * t; ++k) sum[k] = 0;
for (int k = 2 * t; k <= n; ++k) sum[k] = vis[k] * vis[k - 2 * t] + sum[k-1];
for (int d = n; d - (9 * t + 1) >= 1; --d)
{
cnt[d][3] += vis[d - t] * sum[d - 7 * t - 1];
cnt[d - t][2] += vis[d] * sum[d - 7 * t - 1];
}
}
for (int i = 1; i <= m; ++i)
{
printf("%d %d %d %d\n",cnt[a[i]][0], cnt[a[i]][1], cnt[a[i]][2], cnt[a[i]][3]);
}
return 0;
}