A题 Easy h-index
A题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6276
题意: 这题意一开始没有快速理解,wa了两发才明白啥意思。可以理解为,题目给定一个
N
N
N,在给出
a
0
,
a
1
,
a
2
.
.
.
a
n
a_0,a_1,a_2...a_n
a0,a1,a2...an,表示权值为
i
i
i的物品数量有
a
i
a_i
ai件,求最大的
h
h
h,其中
h
h
h满足权值大于
h
h
h的物品数量也大于
h
h
h。
题解: 要注意一个坑点,权值大于
i
i
i的物品数量也可以算在
a
i
a_i
ai当中,所以解法很简单,就是求一个后缀和,然后取
a
i
a_i
ai和
i
i
i中小的那个即可。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
ll a[MAX];
int main() {
ll n;
while (~scanf("%lld", &n)) {
ll maxx = 0;
for (int i = 0; i <= n; i++)
scanf("%lld", &a[i]);
maxx = max(maxx, min(a[n], n));
for (ll i = n - 1; i >= 0; i--) {
a[i] = a[i + 1] + a[i];
maxx = max(maxx, min(a[i], i));
}
printf("%lld\n", maxx);
}
return 0;
}
B题 Higher h-index
B题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6277
题意: 和上一题一样,可以理解为求最大的
h
h
h,其中
h
h
h满足权值大于
h
h
h的物品数量也大于$h,只不过这里题意稍微改变了一下,这里给了一个
n
n
n 和
a
a
a,意味总共有
x
x
x小时,可以选择工作
x
x
x小时得到一篇paper(物品),其中paper的引用次数(权值)为
a
∗
x
a * x
a∗x,并且之后的发的paper可以引用以前发的paper,即之前的paper的引用次数(权值)可以+1,简单的说就是当前这个物品后面还有几个物品,那么他就可以加多少值。
题解: 没有什么好说的,我是纸上手动打表发现规律的。答案就是
(
a
+
n
)
/
2
(a + n) / 2
(a+n)/2。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
int main() {
ll a, b;
while (scanf("%lld%lld", &a, &b) != EOF) {
printf("%lld\n", (a + b) / 2);
}
return 0;
}
C题 Just h-index
C题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6278
题意: 一样的求最大的
h
h
h,其中
h
h
h满足权值大于
h
h
h的物品数量也大于$h,只不过这里是求给定的区间
[
L
,
R
]
[L, R]
[L,R] 中最大的
h
h
h。
题解: 这题是补的。查了一下发现原来有主席树这个东西,学了一天把这题A了。这里做法就是主席树+二分查找,二分这里要求的h(h范围是
[
0
,
m
a
x
(
a
i
)
]
[0, max(a_i)]
[0,max(ai)]),记
m
i
d
=
(
l
+
r
)
/
2
mid = (l + r) / 2
mid=(l+r)/2,每次查询两个状态
T
[
x
−
1
]
T[x - 1]
T[x−1]和
T
[
y
]
T[y]
T[y]之间大于
m
i
d
mid
mid的数的个数
n
u
m
num
num,若
n
u
m
num
num大于等于
m
i
d
mid
mid,说明h可以更大,所以
l
=
m
i
d
+
1
l = mid + 1
l=mid+1,反之
r
=
m
i
d
−
1
r = mid - 1
r=mid−1。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
#include<cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;
int a[MAX], b[MAX], T[MAX], L[MAX << 5], R[MAX << 5], sum[MAX << 5];
int cnt = 0, N, Q, num;
inline int build(int l, int r) {
int root = ++cnt;
sum[root] = 0;
if (l < r) {
int m = (l + r) >> 1;
L[root] = build(l, m);
R[root] = build(m + 1, r);
}
return root;
}
inline int update(int pre, int l, int r, int x) {
int root = ++cnt;
L[root] = L[pre], R[root] = R[pre], sum[root] = sum[pre] + 1;
if (l < r) {
int m = (l + r) >> 1;
if (x <= m)L[root] = update(L[pre], l, m, x);
else R[root] = update(R[pre], m + 1, r, x);
}
return root;
}
inline void ask_interval(int u, int v, int l, int r, int h) {//查找大于h的数目个数
if (h <= b[l]) {//当前数的范围是[b[l], b[r]],所以若h<=b[l]那么这个这个区间内的数都是大于h的,所以num加上区间内的数的个数
num += sum[v] - sum[u];//sum[v] - sum[u]就是区间内数的个数
return;
}
if (h > b[r])return;//与上面相反
int m = (l + r) >> 1;
ask_interval(L[u], L[v], l, m, h);
ask_interval(R[u], R[v], m + 1, r, h);
}
void init() {
cnt = 0;
memset(T, 0, sizeof(T));
memset(L, 0, sizeof(L));
memset(R, 0, sizeof(R));
memset(sum, 0, sizeof(sum));
}
int main() {
while (~scanf("%d%d", &N, &Q)) {
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b + 1, b + 1 + N);
int n = unique(b + 1, b + 1 + N) - b - 1;
T[0] = build(1, n);
for (int i = 1; i <= N; i++) {
int t = lower_bound(b + 1, b + 1 + n, a[i]) - b;
T[i] = update(T[i - 1], 1, n, t);
}
while (Q--) {
int x, y;
scanf("%d%d", &x, &y);
int l = 0, r = b[n], mid, ans = 0;
while (l <= r) {
num = 0;//记录大于mid的数的个数
mid = (l + r) >> 1;
ask_interval(T[x - 1], T[y], 1, n, mid);
if (mid <= num)l = mid + 1, ans = max(ans, mid);//mid <= num说明h还可以更大
else r = mid - 1;//h只能更小
}
printf("%d\n", ans);
}
init();
}
return 0;
}
F题 Sorting
F题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6281
题意: 很水,就是根据题意排个序。
题解: 这里小数的比较我有点迷,wa了一发,所以我直接将比较方法转化为
(
a
p
i
−
1
+
b
p
i
−
1
)
∗
(
a
p
i
+
b
p
i
+
c
p
i
)
<
=
(
a
p
i
+
b
p
i
)
∗
(
a
p
i
−
1
+
b
p
i
−
1
+
c
p
i
−
1
)
(a_{p_i−1} + b_{p_i−1}) * (a_{p_i} + b_{p_i} + c_{p_i}) <= (a_{p_i} + b_{p_i}) * (a_{p_i - 1} + b_{p_i - 1} + c_{p_i - 1})
(api−1+bpi−1)∗(api+bpi+cpi)<=(api+bpi)∗(api−1+bpi−1+cpi−1),转化为整数的比较,然后就过了。但是乘积最大为
24
∗
1
0
18
24 * 10^{18}
24∗1018实际上是大于unsigned long long的,所以数据么。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef unsigned long long ull;
const int MAX = 1e3 + 10;
struct node {
int n;
ull s, c;
}x[MAX];
bool cmp(const node &a, const node &b) {
ull n1 = a.s * (b.s + b.c), n2 = b.s * (a.s + a.c);
if (n1 == n2)return a.n < b.n;
return n1 < n2;
}
int main() {
int n;
ull a, b, c;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; i++) {
x[i].n = i;
scanf("%lld%lld%lld", &a, &b, &x[i].c);
x[i].s = a + b;
}
sort(x + 1, x + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
if (i != 1)printf(" ");
printf("%d", x[i].n);
}
printf("\n");
}
return 0;
}
G题 Just h-index
G题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6282
题意: 给定两个字符串
s
1
,
s
2
s_1,s_2
s1,s2,其中字符串只含有
a
,
b
,
c
a,b,c
a,b,c三种字符,现问字符串
s
1
s_1
s1能否通过变换得到字符串
s
2
s_2
s2,其中变换规则为增加或者删除,只能操作
a
b
a
b
,
a
a
,
b
b
abab, aa, bb
abab,aa,bb。
题解: 首先,注意到字符
c
c
c是不能增加也不能删除的,所以当字符串
s
1
,
s
2
s_1, s_2
s1,s2中字符
c
c
c的个数不同时,肯定不能。
然后注意到,字符串
a
b
ab
ab 可以变换为
b
a
ba
ba (样例就是),这也就说明了一点,在两个字符
c
c
c中间的
a
a
a和
b
b
b的次序不重要,因为他们可以互换位置,因此只需要考虑两个字符
c
c
c之间
a
a
a和
b
b
b的数量的奇偶性,只要
s
1
s_1
s1和
s
2
s_2
s2中同样的两个字符
c
c
c之间
a
a
a出现次数的奇偶性相同并且
b
b
b出现次数的奇偶性相同就可以转换。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
int main() {
char s1[10000 + 50], s2[10000 + 50];
int sn1[10000+50][2], sn2[10000+50][2];
while (~scanf("%s%s", s1, s2))
{
int num1 = 0, num2 = 0, l1 = strlen(s1), l2 = strlen(s2);
for (int i = 0; i < l1; i++)
{
if (s1[i] == 'c')
num1++; //num1为s的c的数量
}
for (int i = 0; i < l2; i++)
{
if (s2[i] == 'c')
num2++; //num2为t的c的数量
}
if (num1 != num2)
printf("No\n");
else
{
num1++;
s1[l1++] = 'c';
s2[l2++] = 'c';
s1[l1] = '\0';
s2[l2] = '\0';
int num = 0, a = 0, b = 0;
for (int i = 0; i < l1; i++)
{
if (s1[i] == 'c')
{
sn1[num][0] = a % 2;
sn1[num++][1] = b % 2;
a = 0, b = 0;
}
if (s1[i] == 'a')a++;
if (s1[i] == 'b')b++;
}
num = 0;
for (int i = 0; i < l2; i++)
{
if (s2[i] == 'c')
{
sn2[num][0] = a % 2;
sn2[num++][1] = b % 2;
a = 0, b = 0;
}
if (s2[i] == 'a')a++;
if (s2[i] == 'b')b++;
}
int flag = 0;
for (int i = 0; i < num1; i++)
{
if (sn1[i][0] != sn2[i][0] || sn1[i][1] != sn2[i][1])
{
flag = 1;
break;
}
}
if (flag == 1)
printf("No\n");
else
printf("Yes\n");
}
}
}
K题 2018
K题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6286
题意: 给定两个区间
[
L
1
,
R
1
]
,
[
L
2
,
R
2
]
[L_1, R_1], [L_2, R_2]
[L1,R1],[L2,R2],求有多少组不同的
x
∈
[
L
1
,
R
1
]
,
y
∈
[
L
2
,
R
2
]
x∈[L_1, R_1], y∈[L_2, R_2]
x∈[L1,R1],y∈[L2,R2]使得
x
∗
y
x*y
x∗y为2018的倍数。
题解: 肯定是先求2018的因数,这里发现只有1 * 2018 和 2 * 1009 这两种情况,所以只需要找各个区间内有几个 1009的倍数 、 2018的倍数和偶数。这里肯定不可能将区间扫完求个数,可以可以用等差数列的思想,比如要找2018倍数的个数,先找第一个2018出现的位置pos,找的到的话2018倍数的个数num = r1 / 2018 - pos1 / 2018 + (r1 % 2018 == 0 ? 1 : 0)。
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<functional>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
int main() {
int l1, l2, r1, r2;
while (scanf("%d%d%d%d", &l1, &r1, &l2, &r2) != EOF) {
int flag1 = 0, flag2 = 0, pos1 = -1, pos2 = -1;
for (int i = l1; i <= r1; i++)
{
if (i % 1009 == 0)
{
flag1 = 1;
pos1 = i;
break;
}
}
for (int i = l2; i <= r2; i++)
{
if (i % 1009 == 0)
{
flag2 = 1;
pos2 = i;
break;
}
}
if (flag1 == 0 && flag2 == 0)
printf("0\n");
else
{
int num1 = 0, num2 = 0, num3 = 0, num4 = 0;
ll sum = 0;
if (flag1 != 0)
{
num1 = r1 / 2018 - pos1 / 2018;
if (pos1 % 2018 == 0)num1++;
num2 = max(0, r1 / 1009 - pos1 / 1009 + 1 - num1);
}
if (flag2 != 0)
{
num3 = r2 / 2018 - pos2 / 2018;
if (pos2 % 2018 == 0)num3++;
num4 = max(0, r2 / 1009 - pos2 / 1009 + 1 - num3);
}
int num5;
if (l1 % 2 == 0 && r1 % 2 == 0)
num5 = (r1 - l1 + 1) / 2 + 1;
else
num5 = (r1 - l1 + 1) / 2;
int num6;
if (l2 % 2 == 0 && r2 % 2 == 0)
num6 = (r2 - l2 + 1) / 2 + 1;
else
num6 = (r2 - l2 + 1) / 2;
sum = sum + 1ll * num1*(r2 - l2 + 1) + 1ll * num3*(r1 - l1 + 1) + 1ll * num2*(num6 - num3) + 1ll * num4*(num5 - num1) - 1ll * num1*num3;
printf("%lld\n", sum);
}
}
return 0;
}
其他题目待补中