A:Sequence with Digits
题意:定义
a
n
+
1
=
a
n
+
m
i
n
D
i
g
i
t
(
a
n
)
∗
m
a
x
D
i
g
i
t
(
a
n
)
a_{n + 1} = a_{n} + minDigit(a_{n}) * maxDigit(a_{n})
an+1=an+minDigit(an)∗maxDigit(an),给定
a
1
a_{1}
a1求
a
k
a_{k}
ak。
数据范围:
1
<
=
a
1
<
=
1
0
18
,
1
<
=
k
<
=
1
0
16
1 <= a_{1} <= 10 ^ {18}, 1 <= k <= 10 ^ {16}
1<=a1<=1018,1<=k<=1016。
题解:只要在中间步骤中某个
a
i
a_{i}
ai存在数位0,就不会再增大了,发现很快就会出现数位0,直接模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a, k;
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%I64d%I64d", &a, &k);
while(k > 1) {
ll Min = 9, Max = 0;
ll b = a;
while(b > 0) {
Min = min(Min, b % 10);
Max = max(Max, b % 10);
b /= 10;
}
if(Min == 0) break;
a += Min * Max;
--k;
}
printf("%I64d\n", a);
}
return 0;
}
B:Young Explorers
题意:有
n
n
n个人,每个人有一个权值
e
i
e_i
ei,表示这个人所在的组的人数必须大于等于
e
i
e_i
ei,对这些人进行分组,对于一个人可以选择不对其进行分组,问最多可以分多少组。
数据范围:
1
<
=
n
<
=
2
∗
1
0
5
,
1
<
=
e
i
<
=
n
1<=n<=2*10^{5},1<=e_i<=n
1<=n<=2∗105,1<=ei<=n。
题解:用桶记录每个权值的人数,从小到大贪心分组,当小权值的人数不够再分一组时,将人数加到大权值中。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
int n, a[maxn], cnt[maxn];
int main() {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) cnt[i] = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", a + i);
cnt[a[i]]++;
}
int ans = 0;
for(int i = 1; i <= n; ++i){
ans += cnt[i] / i;
cnt[i + 1] += cnt[i] % i;
}
printf("%d\n", ans);
}
return 0;
}
C:Count Triangles
题意:给定4个整数
A
,
B
,
C
,
D
A,B,C,D
A,B,C,D,计算边长为
x
,
y
,
z
x,y,z
x,y,z满足
A
<
=
x
<
=
B
<
=
y
<
=
C
<
=
z
<
=
D
A<=x<=B<=y<=C<=z<=D
A<=x<=B<=y<=C<=z<=D的三角形数量。
数据范围:
1
<
=
A
,
B
,
C
,
D
<
=
5
∗
1
0
5
1 <= A,B,C,D<=5*10^5
1<=A,B,C,D<=5∗105。
题解:先枚举
x
x
x,由
z
z
z的上下限确定合法
y
y
y的范围,利用前缀和求出每个
y
y
y有多少种合法的三角形即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 7;
ll sum[maxn];
int main() {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
ll ans = 0;
for(int i = a; i <= b; ++i) {
int Mi = c - i, Ma = d - i;
++sum[Mi + 1];
--sum[Ma + 2];
}
for(int i = 1; i <= c; ++i) sum[i] += sum[i - 1];
for(int i = 1; i <= c; ++i) sum[i] += sum[i - 1];
for(int i = b; i <= c; ++i) ans += sum[i];
printf("%I64d\n", ans);
return 0;
}
D:Game With Array
题意:给两个整数
N
N
N和
S
S
S,构造出一个长度为
N
N
N的序列
a
a
a,其元素之和为
S
S
S,要求
a
a
a的所有子段和不能全部构成
0...
S
/
2
0...S / 2
0...S/2。可以构造输出YES,并输出构造的数组
a
a
a和不能构成的元素
K
K
K。无法构造输出NO。
数据范围:
1
<
=
N
<
=
S
<
=
1
0
6
1<=N<=S<=10^6
1<=N<=S<=106。
题解:当
S
>
=
2
∗
N
S>=2 *N
S>=2∗N时,显然将
a
a
a中元素全部设成
>
=
2
>=2
>=2即可,这样不能构成1。后面大力猜结论,当
S
<
2
∗
N
S < 2*N
S<2∗N时,无法构造出序列
a
a
a(其实是不想证QAQ)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
int a[maxn];
int n, s;
int main() {
scanf("%d%d", &n, &s);
if(s >= 2 * n) {
s -= 2 * n;
puts("YES");
printf("%d", 2 + s);
for(int i = 1; i < n; ++i) printf(" %d", 2);
puts("");
puts("1");
}
else puts("NO");
return 0;
}
E:Restorer Distance
题意:给一个长度为
n
n
n的序列
h
h
h,有3中操作:
1.选择一个位置
i
i
i,使
h
i
=
h
i
+
1
h_i =h_i+1
hi=hi+1,代价为
A
A
A。
2.选择一个位置
i
(
h
i
>
0
)
i(h_i>0)
i(hi>0),使
h
i
=
h
i
−
1
h_i=h_i-1
hi=hi−1,代价为
B
B
B。
3.选择两个位置
i
,
j
(
h
i
>
0
)
i,j(h_i>0)
i,j(hi>0),使
h
i
=
h
i
−
1
,
h
j
=
h
j
+
1
h_i=h_i-1,h_j=h_j+1
hi=hi−1,hj=hj+1,代价为
M
M
M。
求使序列
h
h
h中所有元素都相等的代价最下是多少。
数据范围:
1
<
=
n
<
=
1
0
5
,
1
<
=
A
,
R
,
M
<
=
1
0
4
,
1
<
=
h
i
<
=
1
0
9
1<=n<=10^5,1<=A,R,M<=10^4,1<=h_i<=10^9
1<=n<=105,1<=A,R,M<=104,1<=hi<=109。
题解:这看起来就是有极值的,直接三分最终的元素值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int h[maxn];
int n, A, R, M;
ll check(int x) {
ll cnta = 0, cntb = 0, cntm;
for(int i = 1; i <= n; ++i) {
if(h[i] < x) cnta += x - h[i];
else cntb += h[i] - x;
}
cntm = min(cnta, cntb);
cnta -= cntm;
cntb -= cntm;
return cnta * A + cntb * R + cntm * M;
}
int main() {
scanf("%d%d%d%d", &n, &A, &R, &M);
for(int i = 1; i <= n; ++i) scanf("%d", h + i);
M = min(M, A + R);
int l = 0, r = 1e9 + 7, lmid, rmid;
while(r - l > 2) {
lmid = l + (r - l) / 3;
rmid = r - (r - l) / 3;
if(check(lmid) > check(rmid)) {
l = lmid;
}
else r = rmid;
}
ll ans = 1e18;
while(l <= r) ans = min(ans, check(l++));
printf("%I64d\n", ans);
return 0;
}
F:Guess Divisors Count
题意:交互题,给一个隐藏的
X
(
1
<
=
X
<
=
1
0
9
)
X(1<=X<=10^9)
X(1<=X<=109),每次询问可以给出一个
Q
(
1
<
=
Q
<
=
1
0
18
)
Q(1<=Q<=10^{18})
Q(1<=Q<=1018),问
g
c
d
(
X
,
Q
)
gcd(X,Q)
gcd(X,Q)是多少,最多进行22次询问,最后回答
X
X
X大概有多少个因子,误差允许的范围:设你的答案为
a
n
s
ans
ans,
X
X
X的因子数为
d
d
d,则需要满足下面两个要求之一则算作正确:
1.
∣
a
n
s
−
d
∣
<
=
7
|ans - d| <= 7
∣ans−d∣<=7。
2.
1
2
<
=
a
n
s
d
<
=
2
\frac 12<=\frac {ans}{d}<=2
21<=dans<=2。
题解:首先,要知道可以通过询问
X
X
X中素因子次幂我们可以知道其因子数。因为
X
X
X的范围在
1
0
9
10^9
109,只要知道了
1000
1000
1000以内的
X
X
X的因子数就可以知道
X
X
X大约
2
3
\frac 23
32的因子数,以这个为切入点,将
1000
1000
1000以内的素因子求出,发现
1000
1000
1000内的素数有168个,将每个素因子都乘到它
1000
1000
1000范围内的最大幂次a即
p
a
<
=
1000
p^a<=1000
pa<=1000用来求
X
X
X在
1000
1000
1000内的素因子次幂,又因为询问的数
Q
Q
Q最大范围是
1
0
18
10^{18}
1018,所以可以通过将多个素因子乘在一起询问,减少询问的次数。最后处理完后发现还要26个询问,但题目只要求超过
1
2
\frac 12
21且相差不大于7,所以少询问几个大的素因子无伤大雅。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int query(ll x) {
cout << "? " << x << endl;
int res;
cin >> res;
return res;
}
void answer(int ans) {
cout << "! " << ans << endl;
}
vector<ll> prime, p, q;
int vis[1100];
int main() {
//168个
for(int i = 2; i <= 1000; ++i) {
if(vis[i]) continue;
prime.push_back(i);
for(int j = i; j <= 1000; j += i) vis[j] = 1;
}
int Max = 1024;
for(ll &v : prime) {
ll t = v;
while(t <= Max) t *= v;
t /= v;
p.push_back(t);
}
ll qMax = 1e18;
for(int i = 0; i < p.size();) {
ll x = 1;
while(i < p.size() && qMax / x >= p[i]) {
x *= p[i++];
}
q.push_back(x);
}
//cout << q.size() << endl;
int t;
cin >> t;
while(t--) {
int ans = 1;
for(int i = 0; i < 22; ++i) {
int v = query(q[i]);
for(ll &x : prime) {
int c = 0;
while(v % x == 0) ++c, v /= x;
ans *= (c + 1);
}
}
answer(ans * 2);
}
return 0;
}