Codeforces Round-#373 (Div. 2 && Div. 1) [Codeforces719 && 718]

来源:互联网 发布:哪个软件有鱼眼效果 编辑:程序博客网 时间:2024/05/01 19:55

题目链接
Div. 1
Div. 2
官方题解

719 - A - Vitya in the Countryside

题目大意

一个月有31天,每天月亮的尺寸为0,1,,14,15,14,,1,,第二月又重复上面循环,现给出连续n天的月亮尺寸求判断下一天月亮尺寸的大小变化,不能确定则输出1

思路 - 模拟

按照题意判断即可,注意坑点有最后一天是015需要特判。

代码

#include <cstdio>using namespace std;int n, pre, cur;int main() {    while(1 == scanf("%d", &n)) {        scanf("%d", &cur);        for(int i = 1; i < n; ++i) {            pre = cur;            scanf("%d", &cur);        }        if(cur == 0) {            printf("UP\n");        }        else if(cur == 15) {            printf("DOWN\n");        }        else if(n == 1) {            printf("-1\n");        }        else {            printf("%s\n", pre < cur ? "UP" : "DOWN");        }    }    return 0;}

719 - B - Anatoly and Cockroaches

题目大意

有一个01串,现要求01交替排列,有两种操作,①交换任意两个字符;②将其中一个字符变成相反的字符,求使这个串满足要求时最少的操作次数?

思路 - 贪心

按照题目要求,再枚举奇数位放01,则可以确定01的个数。然后通过统计奇数位和偶数位的01的个数先确定要替换字符,然后贪心地先替换不在正确位置的字符,不够时再替换在正确位置的字符,最后再加上不在正确位置的字符个数即可。答案是两个的较小值。

代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n;int getMin(int cnt_b[3], int cnt_r[3]) {    int num = n / 2 + (n & 1), tmp;//num为r字符的应有值    //令第一个字符为r字符    if(cnt_r[0] + cnt_r[1] <= num) {//若r的字符个数小于等于应有值,则需要将b字符转换成r字符        tmp = num - cnt_r[0] - cnt_r[1];//需要将tmp个b字符转换成r字符        //可以确定在0,2,4...的b字符一定大于等于tmp,否则不满足字符数等于应有值        cnt_b[0] -= tmp;//将所有需要转换的字符都在正确位置转换        tmp += cnt_b[0];//剩余的不在正确位置的b字符需要交换操作    }    else {//需要将r字符转换成b字符        tmp = cnt_r[0] + cnt_r[1] - num;//需要将tmp个r字符转换成b字符        if(cnt_r[1] >= tmp) {            cnt_r[1] -= tmp;        }        else {            cnt_r[1] -= tmp - cnt_r[0];            cnt_r[1] = 0;        }        tmp += cnt_b[0];//剩余的不在正确位置的b字符需要交换操作    }    return tmp;}int cnt_b[3][3], cnt_r[3][3], ans, tmp, num;char s[100003];int main() {    while(1 == scanf("%d", &n)) {        scanf("%s", s);        cnt_b[0][0] = cnt_b[0][1] = cnt_r[0][0] = cnt_r[0][1] = 0;        cnt_b[1][0] = cnt_b[1][1] = cnt_r[1][0] = cnt_r[1][1] = 0;        for(int i = 0; i < n; ++i) {//统计每个字符在奇数位和偶数位出现的次数            if(s[i] == 'b') {                ++cnt_b[0][i & 1];                ++cnt_b[1][i & 1];            }            else {                ++cnt_r[0][i & 1];                ++cnt_r[1][i & 1];            }        }        printf("%d\n", min(getMin(cnt_b[0], cnt_r[0]), getMin(cnt_r[1], cnt_b[1])));    }    return 0;}

618 - A - Efim and Strange Grade

题目大意

给定一个小数,可以对小数部分最多进行t次四舍五入,求能得到的数的最大值?

思路 - 模拟

从小数点后第一个大于等于5的数开始往前四舍五入,注意进位即可。

题解用的是DPdp[i]表示让第i位进位到第i1位所需的最小进位次数。然后对a[i]>=5a[i]==4a[i]<4分别进行状态转移即可,最后找到最小的i使得dp[i]<=t,对a[i]+1即可。

代码

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n, t, pos, ed, carry;char s[200003];int main() {    while(2 == scanf("%d%d", &n, &t)) {        scanf("%s", s + 1);        s[0] = '0';        for(pos = 1; pos <= n; ++pos) {            if(s[pos] == '.') {                break;            }        }        int i;        for(i = 1; i <= n; ++i) {            if(i > pos && '5' <= s[i] && s[i] <= '9') {                if(i - 1 == pos) {                    ++s[i - 2];                    s[i - 1] = '\0';                    i -= 2;                }                else {                    ++s[i - 1];                    if(i > pos) {                        s[i] = '\0';                    }                    --i;                }                carry = (s[i] == '0' + 10) ? 1 : 0;                --t;                break;            }        }        if(i <= n) {            while(i > pos && t > 0 && '5' <= s[i]) {                if(i - 1 == pos) {                    ++s[i - 2];                    s[i - 1] = '\0';                    i -= 2;                }                else {                    ++s[i - 1];                    if(i > pos) {                        s[i] = '\0';                    }                    --i;                }                carry = (s[i] == '0' + 10) ? 1 : 0;                --t;            }        }        while(carry == 1) {            s[i] = '0';            if(i - 1 == pos) {                ++s[i - 2];                s[i - 1] = '\0';                i -= 2;            }            else {                ++s[i - 1];                if(i > pos) {                    s[i] = '\0';                }                --i;            }            carry = (s[i] == '0' + 10) ? 1 : 0;        }        printf("%s\n", s[0] == '0' ? s + 1 : s);    }    return 0;}

718 - C - Sasha and Array

题目大意

给定一个数列a[1],a[2],,a[n],有两个操作:
1 l r x:对a[l],a[l+1],,a[r],分别加上x
2 l r:查询sumri=lf(a[i])f(x)表示斐波那契数列的第i项。

思路 - 线段树 && 矩阵快速幂

肯定能想到用矩阵快速幂求斐波那契数列,但是没有想到利用矩阵快速幂的方法,用线段树维护矩阵。
叶子结点维护f(a[i]),f(a[i]+1),然后矩阵支持分配律,所以非叶子结点是其所有叶子结点的对应项之和,用lazy标记一下即可。
lazy应该也存成矩阵,不能存成指数,否则会TLE。刚开始存成指数,各种优化,还闯到51组数据。。。

代码

#include <cstdio>#include <cstring>#include <algorithm>#define lson (i << 1)#define rson ((i << 1) | 1)using namespace std;const int MAXN = 100003;const int MAX_MAT = 2;const long long MOD = 1e9+7;struct Matrix {    int m[MAX_MAT][MAX_MAT];    bool operator == (const Matrix& b) const {        for(int i = 0; i < MAX_MAT; ++i) {            for(int j = 0; j < MAX_MAT; ++j) {                if(m[i][j] != b.m[i][j]) {                    return false;                }            }        }        return true;    }    Matrix operator + (const Matrix& b) const {        Matrix c;        for(int i = 0; i < MAX_MAT; ++i) {            for(int j = 0; j < MAX_MAT; ++j) {                c.m[i][j] = (m[i][j] + b.m[i][j]) % MOD;            }        }        return c;    }    Matrix operator - (const Matrix& b) const {        Matrix c;        for(int i = 0; i < MAX_MAT; ++i) {            for(int j = 0; j < MAX_MAT; ++j) {                c.m[i][j] = (m[i][j] - b.m[i][j] + MOD) % MOD;            }        }        return c;    }    Matrix operator * (const Matrix& b) const {        Matrix c;        for(int i = 0; i < MAX_MAT; ++i) {            for(int j = 0; j < MAX_MAT; ++j) {                long long res = 0;                for(int k = 0; k < MAX_MAT; ++k) {                    res += ((long long) m[i][k]) * b.m[k][j];                }                c.m[i][j] = res % MOD;            }        }        return c;    }    Matrix operator ^ (long long n) const;};const Matrix FIB = {1, 1,                    0, 0};const Matrix P = {0, 1,                  1, 1,};const Matrix I = {1, 0,                  0, 1,};//由于使用了结构体本身的常量,所以需要定义成外部成员函数Matrix Matrix :: operator ^ (long long n) const {    Matrix m = *this, b = I;    while(n > 0) {        if((n & 1) == 1) {            b = b * m;        }        n = n >> 1;        m = m * m;    }    return b;}struct Node {    int l, r;    Matrix sum, lazy;//刚开始一直把lazy存成指数,导致花式TLE}tr[MAXN << 2];int n, m;int ope, L, R, X;inline void pushDown(int i) {    if(!(tr[i].lazy == I) && tr[i].l != tr[i].r) {//叶子结点不下放,否则会越界        tr[lson].sum = tr[lson].sum * tr[i].lazy;        tr[lson].lazy = tr[lson].lazy * tr[i].lazy;        tr[rson].sum = tr[rson].sum * tr[i].lazy;        tr[rson].lazy = tr[rson].lazy * tr[i].lazy;        tr[i].lazy = I;    }}inline void pushUp(int i) {    tr[i].sum = tr[lson].sum + tr[rson].sum;}void build(int i, int l, int r) {    tr[i].l = l;    tr[i].r = r;    tr[i].lazy = I;    if(l == r) {        scanf("%d", &X);        tr[i].sum = FIB;        tr[i].sum = tr[i].sum * (P ^ (X - 1));        return ;    }    int mid = (l + r) >> 1;    build(lson, l, mid);    build(rson, mid + 1, r);    pushUp(i);}void modify(int i, const Matrix& temp) {    if(L <= tr[i].l && tr[i].r <= R) {        tr[i].sum = tr[i].sum * temp;        tr[i].lazy = tr[i].lazy * temp;        return ;    }    pushDown(i);    int mid = tr[lson].r;    if(mid >= L) {        modify(lson, temp);    }    if(mid < R) {        modify(rson, temp);    }    pushUp(i);}int query(int i) {    if(L <= tr[i].l && tr[i].r <= R) {        return tr[i].sum.m[0][0];    }    pushDown(i);    int mid = tr[lson].r;    int res = 0;    if(mid >= L) {        res = query(lson);    }    if(mid < R) {        res += query(rson);    }    pushUp(i);    return res % MOD;}int main() {    while(2 == scanf("%d%d", &n, &m)) {        build(1, 1, n);        while(m-- >0) {            scanf("%d%d%d", &ope, &L, &R);            if(ope == 1) {                scanf("%d", &X);                modify(1, P ^ X);            }            else {                printf("%d\n", query(1));            }        }    }    return 0;}

718 - D - Andrew and Chemistry

题目大意

给定一颗无根树,现可以添加一个结点,使得添加后形成不同构的树有多少?保证添加前后每个结点的度不能超过4

思路 - 树形DP

题解的方法好巧妙,由于每个结点i至多只和四个结点相连,所以可以用这四个结点(不够时补上虚拟结点)的状态描述结点i的状态。
可以想到O(n2logn)的做法,枚举每个点作为根,然后用DP,从下往上更新每个点的状态值。所有根状态值的不同个数即为答案。
可以不枚举,再从上往下更新状态值就能得到所有点作为根时的状态值。大致是递归前,令当前结点i的以前的子结点v作为其父亲结点,得到结点i以其上部分作为子结点时的状态值。然后就能在O(nlogn)内求得以每个店作为根时的状态值。

代码

#include <cstdio>#include <cstring>#include <map>#include <algorithm>#define lson (i << 1)#define rson ((i << 1) | 1)using namespace std;const int MAXN = 100003;const int INF = 0x3f3f3f3f;struct Vect {    int a[4];    Vect() {        memset(a, 0x3f, sizeof(a));    }    bool operator < (const Vect& x) const {        for(int i = 0; i < 4; ++i) {            if(a[i] != x.a[i]) {                return a[i] < x.a[i];            }        }        return false;    }};struct Node {    int v, nxt;}edge[MAXN << 1];int fir[MAXN], tot;void init() {    memset(fir, -1, sizeof(fir));    tot = 0;}void addEdge(int u, int v) {    edge[tot].v = v;    edge[tot].nxt = fir[u];    fir[u] = tot++;    edge[tot].v = u;    edge[tot].nxt = fir[v];    fir[v] = tot++;}int n, ans, cnt;//a[i]表示只看子结点时的状态值,b[i]表示看所有结点时的状态值int a[MAXN], b[MAXN], deg[MAXN];//deg[i]表示i点的度bool vis[MAXN * 3];//【注意】由于dfsFromDown中cnt至多+n次,dfsFromUp中cnt至多+(n+n)次,所以要开三倍空间map<Vect, int> mp;void dfsFromDown(int u, int p) {//自底向上更新a    Vect cur;    int num = 0, v;    for(int i = fir[u]; i != -1; i = edge[i].nxt) {        v = edge[i].v;        if(v != p) {            dfsFromDown(v, u);            cur.a[num++] = a[v];        }    }    sort(cur.a, cur.a + 4);//按子结点状态的升序排序    if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态        mp[cur] = ++cnt;    }    a[u] = mp[cur];}//status表示p结点以u结点为父亲结点时应有的状态值a[p]void dfsFromUp(int u, int p, int status) {//自顶向下更新b    Vect cur;    int num = 0, v;    for(int i = fir[u]; i != -1; i = edge[i].nxt) {        v = edge[i].v;        if(v == p) {            cur.a[num++] = status;        }        else {            cur.a[num++] = a[v];        }    }    sort(cur.a, cur.a + 4);//按子结点状态的升序排序    if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态        mp[cur] = ++cnt;    }    b[u] = mp[cur];    for(int i = fir[u]; i != -1; i = edge[i].nxt) {        v = edge[i].v;        if(v != p) {            for(int i = 0; i < 4; ++i) {                if(cur.a[i] == a[v]) {                    cur.a[i] = INF;                    break;                }            }            sort(cur.a, cur.a + 4);//按子结点状态的升序排序            if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态                mp[cur] = ++cnt;            }            dfsFromUp(v, u, mp[cur]);            cur.a[3] = a[v];        }    }}int u, v;int main() {    while(1 == scanf("%d", &n)) {        init();        mp.clear();        ans = cnt = 0;        memset(deg, 0, sizeof(deg));        memset(vis, false, sizeof(vis));        for(int i = 1; i < n; ++i) {            scanf("%d%d", &u, &v);            addEdge(u, v);            ++deg[u];            ++deg[v];        }        dfsFromDown(1, 0);        dfsFromUp(1, 0, 0);        for(int i = 1; i <= n; ++i) {            if(deg[i] < 4 && !vis[b[i]]) {//当前结点的度小于4且状态未出现过,则可以添一个结点形成新的不同的树                vis[b[i]] = true;                ++ans;            }        }        printf("%d\n", ans);    }    return 0;}

718 - E -

题目大意

思路 -

代码


0 0
原创粉丝点击