Codeforces Round #694 (Div. 2)

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值