• CSP2020 题解


    T1 儒略历

    考过 (mathrm{CSP2020}) 的都知道,这是一题超级恶心的大模拟。博主因为太菜,到现在为止还不会写正解做法,这里只放部分分,最多到 (mathrm{90pts}).

    40pts

    大概这个分数?不难看出题目的本质就是在询问 (-4713)(1)(1) 日往后 (x) 天是哪一天。考虑最简单的暴力:(Q) 次询问,对于每次询问一天一天往后跳。

    可以先写一个函数判断闰年:

    bool runnian(int x) //判断闰年(英语太烂只能上汉语拼音
    {
        if(x < 0) //公园前闰年判断
        {
            x = -x - 1;
            if(x % 4 == 0) return true;
            return false;
        }
        if(x <= 1582) //公元后分为两段
        {
            if(x % 4 == 0) return true;
            return false;
        }
        else
        {
            if(x % 4 == 0 && x % 100 != 0) return true;
            if(x % 400 == 0) return true;
            return false;
        }
    }
    

    再写一个函数叫 (mathrm{nxt_day}),顾名思义:

    date nxt_day(date x) //下一天 
    {
        x.d++;
        bool fl = runnian(x.y);
        if(fl)
        {
            if(x.m != 2 && x.d > day_num[x.m]) x.m++, x.d = 1;
            else if(x.m == 2 && x.d > day_num[x.m] + 1) x.m++, x.d = 1;
        }
        else if(x.d > day_num[x.m]) x.m++, x.d = 1;
        if(x.m > 12)
        {
            if(x.y != -1) x.y++, x.m = 1; //没有公元 0 年 
            else x.y = 1, x.m = 1;
        }
        if(x.y == 1582 && x.m == 10 && x.d == 5) x.d = 15; //1582年删除的日期 
        return x;
    }
    

    一定要注意各种细节。这个函数比较简单,调过两个小样例基本上就足够了。

    80pts

    (mathrm{40pts}) 完全一样有没有?只需要把询问离线,从小到大排个序,再进行上面的过程:这样复杂度就从 (mathrm{O(Qr)}) 成功的被优化到了 (mathrm{O(r)})

    就不放代码了。

    90pts

    考虑改成一年一年跳。如果 (x>365),就让 (x) 减去 (365),年份 (+1) 即可。

    但是这只是一个基本的思路,代码实现还是有很多恶心的细节。对于 (1582) 年,可以直接暴力地一天一天跳过去。

    还剩下一个 (2)(29) 日的问题。如果当前进来的时候是 (2)(29) 日,就不能直接往后跳 (1) 年,因为下一年可能没有 (2)(29) 日,处理方法是直接跳到下一天。这个地方一定要判断与上一次询问的变化量 x 是否等于 0!. 因为我太垃圾了,这里没有判断,炸了 (20) 分,沦为不如暴力老哥。

    复杂度为 (mathrm{O(frac{r}{365})}),可以通过 (90\%) 的数据。

    考场代码(赛后改动有标记):

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2333333;
    struct date { int y, m, d; } last, ans[N];
    struct que { int r, id; } q[N];
    int Q, r;
    int day_num[23] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    bool cmp(que a, que b) { return a.r < b.r; }
    
    bool runnian(int x) //判断闰年 
    {
        if(x < 0)
        {
            x = -x - 1;
            if(x % 4 == 0) return true;
            return false;
        }
        if(x <= 1582) 
        {
            if(x % 4 == 0) return true;
            return false;
        }
        else
        {
            if(x % 4 == 0 && x % 100 != 0) return true;
            if(x % 400 == 0) return true;
            return false;
        }
    }
    
    date nxt_day(date x) //下一天 
    {
        x.d++;
        bool fl = runnian(x.y);
        if(fl)
        {
            if(x.m != 2 && x.d > day_num[x.m]) x.m++, x.d = 1;
            else if(x.m == 2 && x.d > day_num[x.m] + 1) x.m++, x.d = 1;
        }
        else if(x.d > day_num[x.m]) x.m++, x.d = 1;
        if(x.m > 12)
        {
            if(x.y != -1) x.y++, x.m = 1; //没有公元 0 年 
            else x.y = 1, x.m = 1;
        }
        if(x.y == 1582 && x.m == 10 && x.d == 5) x.d = 15; //1582年删除的日期 
        return x;
    }
    
    /*date query(LL x, date tmp)
    {
        while(x) tmp = nxt_day(tmp), x--;
        return tmp;
    }*/
    
    date nxt_year(date x)
    {
        if(x.y == -1) x.y = 1;
        else x.y++;
        return x;
    }
    
    date query(int x, date tmp)
    {
        //这一句加了 && x 
        if(tmp.m == 2 && tmp.d == 29 && x) x--, tmp = nxt_day(tmp);
        while(x)
        {    
            int num_year = 365;
            bool fl = runnian(tmp.y), fl2 = runnian(nxt_year(tmp).y);
             //如果当前包含了今年的 2 月 29 日
            if(tmp.m <= 2 && fl) if(fl) num_year++;
            if(tmp.m > 2 && fl2) num_year++;
            while(x && tmp.y + 1 == 1582) tmp = nxt_day(tmp), x--;
            while(x && tmp.y == 1582) tmp = nxt_day(tmp), x--;
            if(x < num_year) break;
            x -= num_year, tmp = nxt_year(tmp);
            
        }
        while(x) tmp = nxt_day(tmp), x--;
        return tmp;
    }
    
    int main()
    {
        //freopen("julian.in", "r", stdin);
        //freopen("julian.out", "w", stdout);
        scanf("%d", &Q);
        memset(q, 0, sizeof(q));
        memset(ans, 0, sizeof(ans));
        last = (date) { -4713, 1, 1 };
        memset(q, 0, sizeof(q));
        for(int i = 1; i <= Q; i++) scanf("%d", &q[i].r), q[i].id = i;
        sort(q + 1, q + Q + 1, cmp);
        for(int i = 1; i <= Q; i++)
        {
            ans[q[i].id] = query(q[i].r - q[i - 1].r, last);
            last = ans[q[i].id];
        }
        for(int i = 1; i <= Q; i++)
        {
            if(ans[i].y > 0)
                printf("%d %d %d
    ", ans[i].d, ans[i].m, ans[i].y);
            else
                printf("%d %d %d BC
    ", ans[i].d, ans[i].m, -ans[i].y);
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    

    T2 动物园

    没想到 (mathrm{T2}) 才是真正的签到题。考场上没有看到 (q_i) 互不相同的限制,写了奇奇怪怪的连边做法,炸成了 (mathrm{75pts}). 至今未知为什么此做法的复杂度不正确。

    考场代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #define LL long long //非常玄学,改成 unsigned long long 得分也不会有任何变化
    using namespace std;
    
    const int N = 2333333;
    struct edge { LL nxt, to; } e[N];
    LL n, m, c, k, num, cnt = 0, tot = 0, vis[N], b[N];
    LL a[N], p[N], q[N], use[2333], can[2333], head[N];
    unsigned long long ans = 1;
    
    void add(LL x, LL y)
    {
        e[++cnt] = (edge) { head[x], y };
        head[x] = cnt;
    }
    
    int main()
    {
        scanf("%lld%lld%lld%lld", &n, &m, &c, &k);
        memset(vis, 0, sizeof(vis));
        memset(head, 0, sizeof(head));
        memset(p, 0, sizeof(p));
        memset(q, 0, sizeof(q));
        memset(use, 0, sizeof(use));
        memset(can, 0, sizeof(can));
        for(LL i = 1; i <= n; i++)
        {
            scanf("%lld", &a[i]);
            for(LL j = 0; j < k; j++)
                if(a[i] & (1 << j)) use[j] = 1;
        }
        for(LL i = 1; i <= m; i++)
        {
            scanf("%lld%lld", &p[i], &q[i]);
            b[++tot] = q[i];
        }
        sort(b + 1, b + m + 1);
        for(int i = 1; i <= m; i++)
        {
            q[i] = lower_bound(b + 1, b + m + 1, q[i]) - b - 1;
            add(p[i], q[i]);
            if(use[p[i]]) vis[q[i]] = 1;
        }
        for(LL i = 0; i < k; i++)
        {
            LL fl = 1;
            if(use[i]) { can[i] = 1; continue; }
            for(LL j = head[i]; j; j = e[j].nxt)
            {
                LL v = e[j].to;
                if(!vis[v]) { fl = 0; break; }
            }
            if(fl == 1) can[i] = 1;
        }
        for(LL i = 0; i < k; i++) if(can[i]) ans *= 2;
        printf("%lld", ans - n);
        return 0;
    }
    

    下面开始讲正解。

    很显然,可以按位考虑。设当前位为 (k),如果 (n) 种动物中有第 (k) 位为 (1) 的动物,那么这一位一定有两种选法

    如果当前位有限制条件,而且 (n) 种动物中没有当前位为 (1) 的动物,那么根据 (q_i) 不同,当前饲料一定未被购买。所以这一位只有一种选法

    剩下没有当前位为 (1) 的、且没有限制条件的,有两种选法

    根据格雷码的经验,一定要开 (mathrm{unsigned space long space long}),并且需要特判 (mathrm{k = 64})(mathrm{n = 0}) 的情况(我也不知道为啥)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ULL unsigned long long
    using namespace std;
    
    const int N = 2333333, M = 2333;
    ULL n, m, c, k, a, p, q, ans = 1;
    ULL base[N], vis[M], del[M];
    
    int main()
    {
        scanf("%lld%lld%lld%lld", &n, &m, &c, &k);
        if(k == 64 && n == 0)
        {
            cout << "18446744073709551616";
            return 0;
        }
        memset(vis, 0, sizeof(vis));
        memset(del, 0, sizeof(del));
        base[0] = 1;
        for(int i = 1; i < k; i++) base[i] = base[i - 1] * 2;
        for(int i = 1; i <= n; i++)
        {
            scanf("%lld", &a);
            for(int j = 0; j < k; j++) if(a & base[j]) vis[j] = 1;
        }
        for(int i = 1; i <= m; i++)
        {
            scanf("%lld%lld", &p, &q);
            if(!vis[p]) del[p] = 1;
        }
        for(int i = 0; i < k; i++) if(!del[i]) ans <<= 1;
        printf("%lld", ans - n);
        return 0;
    }
    

    T3 函数调用 && T4 贪吃蛇 : 不会做

  • 相关阅读:
    Listbox与dataGridView的获取鼠标点击事件的区别!!!
    鼠标点击单元格显示在相应文本框中的方法(单元格事件)
    岁月不停~~
    中小学信息学奥林匹克竞赛-理论知识考点--输入输出设备
    中小学信息学奥林匹克竞赛-理论知识考点--计算机组成
    中小学信息学奥林匹克竞赛-理论知识考点--存储容量
    中小学信息学奥林匹克竞赛-理论知识考点--ASCII
    中小学信息学奥林匹克竞赛-理论知识考点--二进制
    scrapy install
    写出输出结果
  • 原文地址:https://www.cnblogs.com/Andy-park/p/14015081.html
Copyright © 2020-2023  润新知