Codeforces Round #694 (Div. 2)
文章目录
A.Strange Partition
向上取整,有余数的数越多越好
合并不会让有余数的数变多,只会变少或不变
所以求max就一个个算,求min就全部合并为一个数
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
#define ll long long
ll rd() {
ll s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
s = s * 10 + c - '0';
c = getchar();
}
return s * f;
}
ll t, n, x, a[maxn], mx, mi;
int main() {
t = rd();
while (t--) {
mx = mi = 0;
n = rd(), x = rd();
for (int i = 1; i <= n; i++) {
a[i] = rd();
if (a[i] % x == 0) mx += a[i]/x;
else mx += a[i]/x+1ll;
mi += a[i];
}
if (mi % x == 0) mi /= x;
else mi = mi / x + 1ll;
cout << mi << " " << mx << "\n";
}
}
B.Strange List
x个q/x对答案的贡献还是q
考虑如何统计答案
当所有数均能整除
x
k
x^k
xk时 答案就包括了所有数的和*(k+1)
当出现第一个不能整除
x
k
x^k
xk的数时,答案是所有数的和*k再加上他前面的所有数的和
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
#define ll long long
ll rd() {
ll s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
s = s * 10 + c - '0';
c = getchar();
}
return s * f;
}
ll t, n, a[maxn], b[maxn], x, ans;
bool flag, flag2;
int main() {
t = rd();
while (t--) {
n = rd(), x = rd();
ans = 0;
ll div = 99999999;
flag = 1;
for (int i = 1; i <= n; i++) {
a[i] = b[i] = rd();
ans += a[i];
}
while (flag) {
for (int i = 1; i <= n; i++) {
if (a[i] != 0 && a[i] % x == 0) {
ans += b[i];
a[i] /= x;
} else {
flag = 0;
break;
}
}
}
cout << ans << "\n";
}
}
C.Strange Birthday Party
让
k
i
k_i
ki大的数选小的C(他们的选择权更多)
这样
k
i
k_i
ki小的数只能选
C
k
i
C_{k_i}
Cki了,这对答案是有利的
那么按
k
i
k_i
ki从大到小排序一个个做就行了
最优性:交换两个相邻的ki不会使答案变更优,但可能使答案变劣
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3e5 + 7;
ll t, n, m, k[maxn], c[maxn], x, ans;
ll rd() {
ll s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
s = s * 10 + c - '0';
c = getchar();
}
return s * f;
}
ll sum;
int tot;
bool cmp(ll a, ll b) {return a > b;}
int main() {
t = rd();
while (t--) {
sum = 0;
n = rd(), m = rd();
for (int i = 1; i <= n; i++) {
k[i] = rd();
}
for (int i = 1; i <= m; i++) {
c[i] = rd();
}
sort(k+1,k+n+1, cmp);
tot = 1;
for (int i = 1; i <= n; i++) {
if (k[i] <= tot) sum += c[k[i]];
else sum += c[tot++];
}
cout << sum << "\n";
}
}
D.Strange Definition
因为
g
c
d
(
a
,
b
)
×
l
c
m
(
a
,
b
)
=
a
×
b
gcd(a,b) \times lcm(a,b)=a \times b
gcd(a,b)×lcm(a,b)=a×b
所以若
l
c
m
(
a
,
b
)
g
c
d
(
a
,
b
)
\frac{lcm(a, b)}{gcd(a, b)}
gcd(a,b)lcm(a,b)是完全平方数,
a
×
b
a \times b
a×b也是完全平方数
每一秒
a
i
a_i
ai会被换成数组中其他所有与他相乘是完全平方数的数的乘积(包括自身)
完全平方数的所有质因子都是偶数次幂
那么两个数adjacent,可以等价于他们的质因数分解结果把质因数的指数对2取模后均相等
也就是他们通过这种方式表示出的数是同一个数。
那么每次相乘相当于把所有adjacent的数的质因数的指数相加然后再模2
那么统计这种表示方式下各个数的个数即可
如果有奇数个 那么他们在1s后,用这种表示方式表示出的还是他们自身。 di不变
如果有偶数个 那么他们在1s后的这种表达方式下表示出的数会变成1 di变成了这种表示方式下1的个数
那么不难发现 w>1的情况与w=1的情况的答案是一样的
因为第一秒时 所有di为偶数的数均变为了1 di为奇数的数会保持不变
1是一直不变的
分解质因数的循环里用long long会被卡常
a
i
a_i
ai只有1e6 直接int就行了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
#define ll long long
map<int, int> cnt;
int t, n, q, ans, cnt1;
ll w, a[maxn];
void div(int x) {
int res = 1, tmp = x;
for (int i = 2; i * i <= x; i++) {
int c = 0;
while (tmp % i == 0) {
tmp /= i;
c++;
}
if (c & 1) res *= i;
}
if (tmp != 1) res *= tmp;
cnt[res]++;
}
ll rd() {
ll s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
s = s * 10 + c - '0';
c = getchar();
}
return s * f;
}
int main() {
t = rd();
while (t--) {
cnt.clear();
cnt1 = 0;
n = rd();
for (int i = 1; i <= n; i++) {
a[i] = rd();
div(a[i]);
}
ans = 0;
for (auto it = cnt.begin(); it != cnt.end(); it++) {
if (it->second % 2 == 0 && it->first != 1)
cnt1 += it->second;
ans = max(ans, it->second);
}
q = rd();
while (q--) {
w = rd();
if (w == 0) {
cout << ans << "\n";
} else {
cout << max(ans, cnt[1] + cnt1) << "\n";
}
}
}
return 0;
}
E.Strange Shuffle
其实可以不关系p的位置 直接先钦定一个位置是p,打表看出p的数据有什么特征的。
假设
p
=
=
⌊
n
2
⌋
p == \lfloor \frac {n}{2} \rfloor
p==⌊2n⌋ 我们直接模拟题目的过程打表
初始时刻,所有数均为k
进过一轮后,p左边的第一个数少了,p右边的第一个数多了
第二轮后,p左边的第二个数和p右边的第二个数受到了影响,分别变多与变少
。。。
以此类推,在所有除位置p以外的数都受到影响前,每次询问会让大于k的数多一个,小于k的数也多一个
最终这个数组会固定下来 但需要经过n/2轮
要求在1000次询问内问出 考虑将序列分块找
100000
\sqrt {100000}
100000大概是330 先做sqrt次询问
然后在sqrt次询问内找到一个大于k或者小于k的数
如果它大于k 就向左移动找p
否则向右移动找p
有一个细节是,如果一开始随机钦定的数等于k,我们要找到一个不等于k的数,每次我们往它的右边数
n
\sqrt n
n个数 它可能会一直在两个数之间跳
(比如样例最终是1 2 3 2 我们可能在2和4之间一直跳)
只有2的时候会出现这种情况 我们把步长调成
n
−
1
\sqrt {n}-1
n−1即可
环型序列把下标调成从0开始比较方便处理
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int n, k, p, b, x, cnt, tmp, tmpp;
int query(int q) {
cout << "? " << q+1 << "\n";
int res;
cin >> res;
fflush(stdout);
return res;
}
int main() {
cin >> n >> k;
b = sqrt(n)-1;
for (int i = 0; i <= b+1; i++) query(i);
//造出了b个小于k和b个大于k的数 注意第一轮询问没有修改
p = (20020123)%n; x = query(p);
while (x == k) {
p = (p + b) % n;
x = query(p);
}
if (x > k) {
tmp = query((p-b+n)%n);
while (tmp > k) {
x = tmp;
p = (p-b+n)%n;
tmp = query((p-b+n)%n);
}
while (x > k) {
p = (p-1+n)%n;
x = query(p);
}
} else if (x < k) {
tmp = query((p+b)%n);
while (tmp < k) {
x = tmp;
p = (p+b)%n;
tmp = query((p+b)%n);
}
while (x < k) {
p = (p+1)%n;
x = query(p);
}
}
cout << "! " << p+1 << endl;
return 0;
}
F. Strange Housing
题意:求图的一个点独立集,满足包括这些点的边能够覆盖图中所有点
暴力
每次遍历一个点时,如果他相邻的所有点都未被选,那么我们选上这个点
这样选出来的点一定是符合条件的
如果无解 就会有点未被访问到
正确性:选出的点一定不相邻,访问过的点如果未被选中,他旁边一定有被选中的点。仅当图不连通的时候无解
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
struct edge {
int v, nxt;
}e[maxn << 1];
int p[maxn], eid, t, n, m, stk[maxn], top, c[maxn], vis[maxn];
void init() {
for (int i = 1; i <= n; i++)
p[i] = -1, c[i] = vis[i] = 0;
eid = top = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid++;
}
void bfs() {
queue<int> q;
q.push(1);
vis[1] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
bool flag = 1;
for (int i = p[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
if (c[v] == 2) flag = 0;
}
if (flag) c[u] = 2, stk[++top] = u;
else c[u] = 1;
}
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u,&v);
insert(u, v);
insert(v, u);
}
bfs();
for (int i = 1; i <= n; i++) {
if (c[i] == 0) {
puts("NO");
goto gg;
}
}
puts("YES");
printf("%d\n", top);
for (int i = 1; i <= top; i++) {
printf("%d ", stk[i]);
}
printf("\n");
gg: continue;
}
}