Educational Codeforces Round 37 [Codeforces920]

题目链接
官方题解

A. Swap Adjacent Elements

题目大意

在长度为 n n 的草坪上,有k个水龙头,第 i i 个水龙头位置为xi,水龙头的水在打开后第 j j 秒会对[x-(j-1), x+(j-1)]内的所有草坪洒水。现在同时打开所有水龙头,求最短多少秒时所有草坪都能被洒到水?

思路 - 模拟

相邻两个水龙头之间的草坪全部能被洒水需要curlast2+1秒,左端点处能在 x1 x 1 内将左边的草坪洒上水,又端点出能在 nxk+1 n − x k + 1 内将右边的草坪洒上水,则答案为所有值中的最大值。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 203;

int n, k, ans, last, cur;

int main() {
    int T;
    scanf("%d", &T);
    while(T-- > 0) {
        scanf("%d%d", &n, &k);
        scanf("%d", &last);
        ans = last;
        while(--k > 0) {
            scanf("%d", &cur);
            ans = max(ans, ((cur - last) >> 1) + 1);
            last = cur;
        }
        ans = max(ans, n - last + 1);
        printf("%d\n", ans);
    }
}

B. Tea Queue

题目大意

n n 个人在排队,第i个人在 li l i 时到达队尾,若在 ri r i 时还未在队首,则必须空手离开;队首的人会获得茶,然后在下一秒离开;求每个人端着茶的时间,不存在则为 0 0

思路 - 模拟

模拟每个人入队后的情况:
①若当前可获得时间time<=r,则能在 max(l,time) m a x ( l , t i m e ) 获得茶,下一次可获得茶的时间为 time=max(l,time)+1 t i m e = m a x ( l , t i m e ) + 1
②如当前可获得时间 time>r t i m e > r ,则不能获得茶而离开,结果为 0 0

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 1003;

int n, time;
int l, r, ans[MAXN];

int main() {
    int T;
    scanf("%d", &T);
    while(T-- > 0) {
        scanf("%d", &n);
        time = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &l, &r);
            if(r >= time) {
                ans[i] = max(l, time) ;
                time = ans[i] + 1;

            }
            else {
                ans[i] = 0;
            }
        }
        for(int i = 1; i <= n; ++i) {
            printf("%d%c", ans[i], i == n ? '\n' : ' ');
        }
    }
}

C. Swap Adjacent Elements

题目大意

有一个长度为n的数组,数组内为 1 n 1   n 的一个排列,现有一个操作:交换 xi x i xi+1 x i + 1 (无次数限制),在给定长度为 n1 n − 1 01 01 串 0 0 表示数组中相同下标的数不能进行这样的操作;1表示数组中相同下标的数能进行这样的操作。求当前数组能否变成一个递增的数组?

思路 - 模拟

很容易就能发现若存在连续为 1 1 的区间[l,r],则 [l,r+1] [ l , r + 1 ] 内的数能交换到该区间内的任意以一个位置,所以以 0 0 将数组划分为多个区间,若每个区间内的数都是其区间内的一个下标,则最终能变成一个递增的数组。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 200003;

int n, a[MAXN], last;
char s[MAXN];
int nxt[MAXN];
bool border;

bool judge() {
    s[n] = '0';
    for(int i = n; i >= 1; --i) {
        if(s[i] == '0') {
            nxt[i] = i;
        }
        else {
            nxt[i] = nxt[i + 1];
        }
    }
    border = false;
    for(int i = 1; i <= n; ++i) {
        if(a[i] > nxt[i]) {
            return false;
        }
        if(s[i] == '0') {
            if(!border && a[i] != i) {
                return false;
            }
            border = false;
        }
        else {
            if(a[i] == nxt[i]) {
                border = true;
            }
        }
    }
    return true;
}

int main() {
    while(1 == scanf("%d", &n)) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
        }
        scanf("%s", s + 1);
        printf("%s\n", judge() ? "YES" : "NO");
    }
}

E. Connected Components?

题目大意

给定n个的顶点的无向图,以及图中不存在的边,求该无向图的联通分量的个数及每个联通分量的大小?

思路 - dfs

很容易就能想到 dfs d f s 求联通分量,但是由于该无向图是密集图,不能通过遍历边的方式进行搜索,只能通过遍历点的方式进行搜索。所以需要维护未使用的点集(我使用链表实现,并通过并查集思想快速获得下一个未使用的顶点),每次进入 dfs d f s 时,对于当前顶点 u u ,遍历未使用的点v,如果 u u v存在边,则移除点 v v ,递归即可。

代码

#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>

using namespace std;

const int MAXN = 200003;

int n, m, cnt;
int ans[MAXN];
set<int> edge[MAXN];
int nxt[MAXN];
bool vis[MAXN];

int getNxt(int cur) {
    if(vis[nxt[cur]]) {
        nxt[cur] = getNxt(nxt[cur]);
    }
    return nxt[cur];
}

int dfs(int u) {
    int res = 0, v;
    for(v = getNxt(0); v <= n; v = getNxt(v)) {
        if(edge[u].find(v) == edge[u].end()) {
            vis[v] = true;
            res += dfs(v) + 1;
        }
    }
    return res;
}

int main() {
    int u, v;
    while(2 == scanf("%d%d", &n, &m)) {
        vis[n + 1] = false;
        nxt[0] = 1;
        for(int i = 1; i <= n; ++i) {
            edge[i].clear();
            vis[i] = false;
            nxt[i] = i + 1;
        }
        while(m-- > 0) {
            scanf("%d%d", &u, &v);
            edge[u].insert(v);
            edge[v].insert(u);
        }
        cnt = 0;
        for(v = getNxt(0); v <= n; v = getNxt(v)) {
            vis[v] = true;
            ans[cnt++] = dfs(v) + 1;
        }
        sort(ans, ans + cnt);
        printf("%d\n", cnt);
        for(int i = 0; i < cnt; ++i) {
            printf("%d%c", ans[i], i == cnt - 1 ? '\n' : ' ');
        }
    }
}

F. SUM and REPLACE

题目大意

给定长度为n的数组,有两个操作:
①将 [l,r] [ l , r ] 内的所有数变成其因子的个数
②求 [l,r] [ l , r ] 内所有数的和

思路 - 线段树

看到区间更新和查询,很容易就能想到线段树。
但是要注意更新的特殊性,就能发觉每个数减少的很快,最多 6 6 次之后就不再变化,所以在更新前,要判断当前线段内的数是否存在可以继续更新的值(只有12不能进行更新)。而这样对每个点的更新操作最多只有 6 6 次,所以可以放心更新。

对于每个点维护两个值:mx表示线段内的最大值,用于更新剪枝; sum s u m 表示线段内所有数的和,用于查询结果。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

#define lson (i << 1)
#define rson ((i << 1) | 1)

using namespace std;

const int MAXN = 300003;
const int MAX_VALUE = 1000003;

int d[MAX_VALUE], L, R;

struct Node {
    int l, r;
    int mx;
    long long sum;
}tr[MAXN << 2];

void build(int i, int l, int r) {
    tr[i].l = l;
    tr[i].r = r;
    if(l == r) {
        scanf("%d", &tr[i].mx);
        tr[i].sum = tr[i].mx;
        return ;
    }

    int mid = (l + r) >> 1;
    build(lson, l , mid);
    build(rson, mid + 1, r);
    tr[i].mx = max(tr[lson].mx, tr[rson].mx);
    tr[i].sum = tr[lson].sum + tr[rson].sum;
}

void update(int i) {
    if(tr[i].l == tr[i].r) {
        tr[i].sum = tr[i].mx = d[tr[i].mx];
        return ;
    }

    if(L <= tr[lson].r && tr[lson].mx > 2) {
        update(lson);
    }
    if(tr[rson].l <= R && tr[rson].mx > 2) {
        update(rson);
    }
    tr[i].mx = max(tr[lson].mx, tr[rson].mx);
    tr[i].sum = tr[lson].sum + tr[rson].sum;
}

long long query(int i) {
    if(L <= tr[i].l && tr[i].r <= R) {
        return tr[i].sum;
    }

    long long ans = 0;
    if(L <= tr[lson].r) {
        ans += query(lson);
    }
    if(tr[rson].l <= R) {
        ans += query(rson);
    }
    return ans;
}

void init() {
    memset(d, 0, sizeof(d));
    for(int i = 1; i < MAX_VALUE; ++i) {
        for(int j = i; j < MAX_VALUE; j += i) {
            ++d[j];
        }
    }
}

int n, m;
int type;

int main() {
    init();
    while(2 == scanf("%d%d", &n, &m)) {
        build(1, 1, n);
        while(m-- > 0) {
            scanf("%d%d%d", &type, &L, &R);
            if(type == 1) {
                update(1);
            }
            else {
                printf("%I64d\n", query(1));
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值