Codeforces Round #307 (Div. 2)

唉最近状态越来越差。。。

A. GukiZ and Contest

给你n个人的分数然后输出排名。
一遍排序搞定。

#include <bits/stdc++.h>

using namespace std;

struct Node{
    int val, pos;
    bool operator < (const Node &n) const {
        return val > n.val;
    }
};

int n;
Node arr[2005];
map<int, int> vis;
int ans[2005];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> arr[i].val;
        arr[i].pos = i;
    }
    sort(arr + 1, arr + n + 1);
    int tot = 0;
    for (int i = 1; i <= n; i++) {      
        int t = arr[i].val;
        if (vis[t]) {
            ans[arr[i].pos] = vis[t];
            tot++;
        }
        else {
            vis[t] = ++tot;
            ans[arr[i].pos] = tot;
        }
    }
    for (int i = 1; i <= n; i++)
        cout << ans[i] << " \n"[i == n];
    return 0;
}

B. ZgukistringZ

3个串a,b,c,可以将a任意重组,要求重组之后包含的无重叠部分的b串和c串的个数最多,问个数最多是多少。

计算a串26个字母分别出现了多少次,然后枚举b串的个数,计算最多能放多少c串。

#include <bits/stdc++.h>

using namespace std;

char a[100005], b[100005], c[100005];
int cnta[30];
int cntb[30];
int cntc[30];
int temp[30];
int main() {
    cin >> a >> b >> c;
    int lena = strlen(a);
    int lenb = strlen(b);
    int lenc = strlen(c);
    for (int i = 0; i < lena; i++) {
        char c = a[i];
        cnta[c - 'a']++;
        temp[c - 'a']++;
    }
    for (int i = 0; i < lenb; i++) {
        cntb[b[i] - 'a']++;
    }
    for (int i = 0; i < lenc; i++) {
        cntc[c[i] - 'a']++;
    }
    int mx = 0;
    int mxi = 0, mxj = 0;
    for (int i = 0; i < lena; i++) {
        int cc = 0x3f3f3f3f;
        for (int j = 0; j < 26; j++) {
            if (!cntc[j]) continue;
            int t = cnta[j] / cntc[j];
            cc = min(cc, t);
        }
        if (i + cc > mx) {
            mxi = i;
            mxj = cc;
            mx = i + cc;
        }
        bool flag = false;
        for (int k = 0; k < 26; k++) {
            cnta[k] -= cntb[k];
            if (cnta[k] < 0) {
                flag = true;
                break;
            }
        }
        if (flag) break;
    }
    for (int i = 0; i < mxi; i++) {
        printf("%s", b);
    }
    for (int i = 0; i < mxj; i++) {
        printf("%s", c);
    }
    for (int i = 0; i < 26; i++) {
        for (int j = 0; j < temp[i] - mxi * cntb[i] - mxj * cntc[i]; j++)
            printf("%c", 'a' + i);
    }
    cout << endl;
    return 0;
}

C. GukiZ hates Boxes

n堆货物摆成一排,有m个人,所有人一开始在所有货物的左边。

每一分钟每个人可以有两种选择:

  1. 向右走一格。

  2. 把他当前所在位置的货物搬走一个。

问把所有货物都搬走最少需要多少分钟。

二分答案,判断能否在一定时间内搬完所有货物,过程如下:

对于每一个人,先让他走到最后一个有货物的位置,然后剩余的时间全部用来搬货物,先搬最后一堆,如果还有时间就搬倒数第二堆(依次类推)。注意他剩了多少时间一定能搬多少货物,因为在走到最后的过程中,可以随时花费一分钟用来搬货物。

#include <bits/stdc++.h>

using namespace std;

int n;
long long m;
long long arr[100005];
long long temp[100005];
bool solve(long long mid) {
    for (int i = 1; i <= n; i++)
        temp[i] = arr[i];
    int pos = n;
    long long tot = 0;
    while (pos > 0) {
        while (!temp[pos] && pos) pos--;
        tot++;
        long long tim = mid - pos;
        if (tim <= 0 && temp[pos]) return false;
        while (tim >= temp[pos] && pos) {
            tim -= temp[pos];
            temp[pos--] = 0;
        }
        temp[pos] -= tim;
        if (tot > m) return false;
    }
    return tot <= m;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> arr[i];
    }
    long long l = 0, r = 2000000000000000;
    while (l < r) {
        long long mid = l + r >> 1;
        if (solve(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << endl;
    return 0;
}

D. GukiZ and Binary Operations

问有多少个长度为n的数组a,数组中的每个元素严格小于 2l ,满足 (a1&a2)|(a2&a3)|...|(an1&an)=k?

输出方案数模m。

太神了我一点也不会。

我们枚举k的每一位,方案数就是每一位的方案数的乘积。

如果这一位是0,则说明数组一定没有连续两个元素是1。设 dp[i] 表示到第i位的方案数,很显然 dp[i]=dp[i1]+dp[i2] ,用矩阵计算。

如果这一位是1,则说明数组中至少有一个两个连续的1,呃。。。不就是 2ndp[n] 么。

好了完了。

#include <bits/stdc++.h>

using namespace std;

long long n, k, l, m;

struct matrix{
    int n, m;
    long long arr[2][2];
    matrix(int a, int b) {
        n = a, m = b;
        memset(arr, 0, sizeof(arr));
    }
};

matrix operator * (matrix a, matrix b) {
    matrix c(a.n, b.m);
    for (int i = 0; i < a.n; i++) {
        for (int j = 0; j < b.m; j++) {
            for (int p = 0; p < a.m; p++) {
                long long t = a.arr[i][p] * b.arr[p][j] % m;
                c.arr[i][j] += t;
                c.arr[i][j] %= m;
            }
        }
    }
    return c;
}

matrix pow(matrix a, long long b) {
    matrix c(2, 2);
    c.arr[0][0] = c.arr[1][1] = 1;
    while (b) {
        if (b & 1) c = c * a;
        a = a * a;
        b >>= 1;
    }
    return c;
}

bool judge() {
    long long t = k;
    for (int i = 0; i < l; i++) t >>= 1;
    return t == 0;
}


long long DP() {
    matrix ans(1, 2);
    ans.arr[0][0] = ans.arr[0][1] = 1;
    matrix base(2, 2);
    base.arr[0][1] = base.arr[1][0] = base.arr[1][1] = 1;
    base = pow(base, n);
    ans = ans * base;
    return ans.arr[0][1];
}

long long power(long long a, long long b) {
    long long ans = 1;
    while (b) {
        if (b & 1)
            ans = ans * a % m;
        a = a * a % m;
        b >>= 1;
    }
    return ans;
}

int main() {
    cin >> n >> k >> l >> m;
    if (!judge()) {
        cout << 0 << endl;
        return 0;
    }
    long long ans = 1;
    long long a = DP();
    long long b = (power(2ll, n) - a + m) % m;
    for (int i = 0; i < l; i++) {
        if ((k >> i) & 1)
            ans = ans * b % m;
        else ans = ans * a % m;
    }
    cout << ans % m << endl;
    return 0;
}

E. GukiZ and GukiZiana

一个长度为n的序列,要求维护两种操作:

  1. 1 l r x. 把[l, r]中的所有数加上x。

  2. 2 y. 询问j - i的最大值使a[i] = a[j] = y。

分块。

把数组分成 n 块,每块 n 个数,然后对每一块排序。

对于操作1,如果[l, r]覆盖了某一块,则给这一块加上一个标记。

如果[l, r]覆盖了某一块的一部分则暴力加上x。

对于操作2,枚举每一块用lower_bound查找值。

第一次写分块,如何装作经常写的样子?写的不好见谅。

#include <bits/stdc++.h>

#define pli pair<long long, int>
#define mp(i, j) make_pair(i, j)

using namespace std;

const int MAXN = 500005;

int n, q;
long long arr[MAXN];
int blocks;
long long tag[MAXN];

vector<pli > mi[770], mx[770];

void rebuild(int id) {
    mi[id].clear();
    mx[id].clear();
    for (int i = id * blocks; i < (id + 1) * blocks && i < n; i++) {
        arr[i] += tag[id];
        mi[id].push_back(mp(arr[i], i));
        mx[id].push_back(mp(arr[i], -i));
    }
    sort(mi[id].begin(), mi[id].end());
    sort(mx[id].begin(), mx[id].end());
    tag[id] = 0;
}

int getmin(long long v) {
    for (int i = 0; i < blocks; i++) {
        long long t = v - tag[i];
        vector<pli >::iterator it = lower_bound(mi[i].begin(), mi[i].end(), mp(t, 0));
        if (it != mi[i].end() && (it -> first == t)) return it -> second;
    }
    return -1;
}

int getmax(long long v) {
    for (int i = blocks - 1; i >= 0; i--) {
        long long t = v - tag[i];
        vector<pli >::iterator it = lower_bound(mx[i].begin(), mx[i].end(), mp(t, -10000000));
        if (it != mx[i].end() && (it -> first == t)) return -it -> second;
    }
    return -1;
}

int main() {
    cin >> n >> q;
    blocks = sqrt(n) + 2;
    for (int i = 0; i < n; i++)
        cin >> arr[i];
    for (int i = 0; i < blocks; i++)
        rebuild(i);
    int op, l, r;
    long long v;
    for (int i = 0; i < q; i++) {
        cin >> op;
        if (op == 1) {
            cin >> l >> r >> v;
            l--, r--;
            if (l / blocks == r / blocks) {
                for (int j = l; j <= r; j++)
                    arr[j] += v;
                rebuild(l / blocks);
            } else {
                int id = l / blocks + 1;
                while (l / blocks != id) {
                    arr[l++] += v;
                }
                rebuild(l / blocks - 1);
                while (l + blocks < r) {
                    tag[l / blocks] += v, l += blocks;
                }
                while (l <= r) arr[l++] += v;
                rebuild(r / blocks);
            }
        } else {
            cin >> v;
            l = getmin(v);
            r = getmax(v);
            if (l < 0) cout << -1 << endl;
            else cout << r - l << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值