• 联赛前第七阶段总结


    和12差70分,模拟赛21不知道挂了多少,模拟赛24挂了80,事实证明挂一场考试没什么事,挂两场我就不行啦
    由于正在专心改T1的时候老姚催总结,所以口糊了几行上去

    总结
    和第12差70分,昨天上午挂了80分。。。
    
    这个阶段开局28名,本来可以追上来,结果因为用vector越界然后RE了,就止步于15名了。
    
    挂一场其实没啥,挂两场就不行了,还是太菜了
    
    问题就是改题效率太低,不会的一定要抓紧时间看题解搞懂
    

    晚间测试13

    阶段排名 15

    暴踩std!!!

    A Dove 打扑克

    • nmlog搞了65,然后就去看T2了,和正解好像只差一个set

    • 比正解还多了个树状数组。

    • set里维护都有那些集合大小出现过,然后枚举就好,直接双指针前缀和,就不用树状数组了。

    Code
    #include <set>
    #include <cstdio>
    #define abs(a) ({int xx = a; xx < 0 ? -xx : xx;})
    
    const int N = 1e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    std::set<int> s;
    std::set<int>::iterator it1, it2;
    int n, m, f[N], siz[N], b[N], size;
    long long ans;
    
    int Find(int x) {
        return x == f[x] ? x : (f[x] = Find(f[x]));
    }
    
    int main() {
        freopen("cards.in", "r", stdin);
        freopen("cards.out", "w", stdout);
        n = read(); m = read();
        for (int i = 1; i <= n; ++i)
            f[i] = i, siz[i] = 1;
        size = b[1] = n;
        s.insert(1);
        while (m--) {
            int od = read();
            if (od == 1) {
                int x = Find(read()), y = Find(read());
                if (x == y) continue;
                if (!--b[siz[x]]) s.erase(siz[x]);
                if (!--b[siz[y]]) s.erase(siz[y]);
                f[x] = y; siz[y] += siz[x];
                siz[x] = 0;
                if (!b[siz[y]]++) s.insert(siz[y]);
                size--;
            }
            else {
                int x = read(), sum = 0; ans = 0;
                if (!x) {
                    printf("%lld
    ", 1LL * size * (size - 1) / 2);
                    continue;
                }
                it1 = it2 = s.begin();
                for (; it2 != s.end(); ++it2) {
                    while (it1 != s.end() && *it1 + x <= *it2) sum += b[*it1++];
                    /*if (*it1 + x <= *it2) */ans += 1LL * sum * b[*it2];
                }
                printf("%lld
    ", ans);
            }
        }
        return 0;
    }
    

    B Cicada 与排序 (Unaccepted)

    • 神奇的题,瞎搞了10分。



    联赛模拟测试24

    阶段排名 15

    本来就是靠T3拉分,结果它挂了80~90分,和第12差距70分,看来要回归最初的美好了。

    A 最大或

    • 就从前往后扫到一位不同的,后面就全是1就行了,挺好想的。
    Code
    #include <cstdio>
    #define int long long 
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    signed main() {
        freopen("maxor.in", "r", stdin);
        freopen("maxor.out", "w", stdout);
        int T = read();
        while (T--) {
            int l = read(), r = read(), s = 0;
            for (int i = 1LL << 59, g = 0; i; i >>= 1) {
                if (!(l & i) && (r & i)) g = 1;
                if (g || ((l & i) && (r & i))) s += i;
            }
            printf("%lld
    ", s);
        }
        return 0;
    }
    

    B 答题

    • 写的50分暴力只给了30分,出题人为了卡分连数据范围也不遵守了,我也是无fuck说了

    • 要知道的是题目的意思就是让求 n 个数组成的 (2^n) 个数第 (left lfloor p*(2^n) ight floor) 小的数

    • 正解是meet in the middle,先把前n/2个数组成的数记录下来,后面的数记录下来,二分第k小的数是多少,双指针查询就可以了

    Code
    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    
    typedef long long ll;
    const int N = 1.1e6;
    int n, a[45], f1[N], f2[N], s1, s2, n2, sum, l, r;
    double p;
    ll k;
    
    void Dfs1(int x, int s) {
        if (x > n2) return f1[++s1] = s, void();
        Dfs1(x + 1, s); Dfs1(x + 1, s + a[x]);
    }
    
    void Dfs2(int x, int s) {
        if (x > n) return f2[++s2] = s, void();
        Dfs2(x + 1, s); Dfs2(x + 1, s + a[x]);
    }
    
    bool Judge(int x, ll s = 0) {
        for (int i = 1, j = s2; i <= s1; ++i) {
            for (; j >= 1 && f1[i] + f2[j] > x; --j);
            if ((s += j) >= k) return 1;
        }
        return 0;
    }
    
    int main() {
        freopen("answer.in", "r", stdin);
        freopen("answer.out", "w", stdout);
        scanf("%d%lf", &n, &p);
        n2 = n / 2;
        k = ceil(p * (1LL << n));
        for (int i = 1; i <= n; ++i)
            scanf("%d
    ", &a[i]), r += a[i];
        Dfs1(1, 0); Dfs2(n2 + 1, 0);
        std::sort(f1 + 1, f1 + s1 + 1);
        std::sort(f2 + 1, f2 + s2 + 1);
        while (l < r) {
            int mid = l + r >> 1;
            if (Judge(mid)) r = mid;
            else l = mid + 1;
        }
        printf("%d
    ", l);
        return 0;
    }
    

    C 联合权值·改

    • 主要是求和的时候会被卡,剩下的都可过,可是for循环的时候v写的<=vecotr的size,然后就RE 5分,挂了80~90分

    • 就是一个三元环的问题,学会了n根号n不重复的枚举三元环就解决了,我连hash判联通都想到了,我和正解也就差个这个,加上就90了,

    • 90是因为本来时限1000的题让某队改成了200,到我交的时候改成了100,然后我有两个点99ms,使劲卡常A掉了

    Code
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define re register
    
    typedef long long ll;
    const int N = 3e4 + 5, M = 59999;
    
    int read(re int x = 0, re int f = 1, re char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    std::vector<int> t[N], e[N];
    struct Hash {
        int next, x, y;
    }h[N<<1];
    int head[N<<1], hac;
    
    void Add(int x, int y) {
        t[x].push_back(y);
        int ha = (x * N + y) % M;
        h[++hac] = (Hash) {head[ha], x, y};
        head[ha] = hac;
    }
    
    bool Find(int x, int y) {
        int ha = (x * N + y) % M;
        for (re int i = head[ha]; i; i = h[i].next)
            if (h[i].x == x && h[i].y == y)
                return 1;
        return 0;
    }
    
    int n, m, a[N], od, in[N], mx;
    long long s;
    
    bool Cmp(const int &x, const int &y) {
        return a[x] > a[y];
    }
    
    int main() {
        freopen("link.in", "r", stdin);
        freopen("link.out", "w", stdout);
        n = read(), m = read(); od = read();
        while (m--) {
            int x = read(), y = read();
            Add(x, y); Add(y, x);
            in[x]++, in[y]++;
        }
        for (re int i = 1; i <= n; ++i)
            a[i] = read();
        for (re int x = 1; x <= n; ++x) {
            for (re int i = 0; i < t[x].size(); ++i) {
                int y = t[x][i];
                if (in[y] > in[x] || (in[y] == in[x] && y > x))
                    e[x].push_back(y);
            }
        }
        for (re int x = 1; x <= n; ++x) {
            if (t[x].size() < 2) continue;
            if (od != 2) {
                std::sort(t[x].begin(), t[x].end(), Cmp);
                int w = a[t[x][0]], w2 = 0;
                for (re int i = 1; i < t[x].size(); ++i)
                    if (!Find(t[x][0], t[x][i]))
                        w2 = a[t[x][i]], i = t[x].size();
                if ((w *= w2) > mx) mx = w;
            }
            if (od != 1) {
                ll s1 = 0, s2 = 0;
                for (re int i = 0; i < t[x].size(); ++i)
                    s1 += a[t[x][i]], s2 += a[t[x][i]] * a[t[x][i]];
                s += s1 * s1 - s2;
                for (re int i = 0; i < e[x].size(); ++i) {
                    int y = e[x][i];
                    for (re int j = 0; j < e[y].size(); ++j) {
                        int z = e[y][j];
                        if (!Find(x, z)) continue;
                        s -= 2LL * a[x] * a[y] + 2LL * a[x] * a[z] + 2LL * a[y] * a[z];
                    }
                }
            }
        }
        printf("%d
    %lld
    ", mx, s);
        return 0;
    }
    

    D 你相信引力吗

    • 不会写,就写了个30分n2暴力。

    • 首先可以想到一个冰锥会和两边不降的冰锥,直到第一个高于自己的冰锥构成危险冰锥对

    • 求危险冰锥对数,那就只计算前面的冰锥,那显然维护一个单调不升的单调栈就可以了

    • 题目中还提到是环,考虑不可能跨过最高点造成贡献,就直接从最高点断开计算

    • 还有重复的情况,就另开一个数组记录之前与当前高度相同的数量

    Code
    #include <cstdio>
    
    const int N = 5e6 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    int n, mx, a[N<<1], stk[N], s[N], tp;
    long long ans;
    
    int main() {
        freopen("jolyne.in", "r", stdin);
        freopen("jolyne.out", "w", stdout);
        n = read();
        for (int i = 1; i <= n; ++i) {
            a[i] = a[i+n] = read();
            if (a[i] > a[mx]) mx = i;
        }
        stk[tp=1] = a[mx];
        s[1] = 1;
        for (int i = mx + 1; i <= mx + n - 1; ++i) {
            for (; tp && stk[tp] < a[i]; --tp) ans++;
            if (stk[tp] == a[i]) ans += s[tp] + (a[i] != a[mx]);
            else ans++;
            stk[++tp] = a[i];
            s[tp] = a[i] == stk[tp-1] ? s[tp-1] + 1 : 1;
        }
        for (; tp > 2 && stk[tp] != stk[2]; --tp) ans++;
        printf("%lld
    ", ans);
        return 0;
    }
    

    联赛模拟测试23

    阶段排名 15

    A 数列

    • 一眼就是扩展欧几里得,可是忘了咋算,就yy了个逆元求出x,y的一组解,可是找不到绝对值和最小的解,就拿了30分

    • 在a<b的情况求出特解后让x尽可能接近0即可


    B 数对

    • 本来打的n3的,拿了0分,考完发现理解错题了,上次就是什么数对理解错了,这次又理解错了,信息奥赛考数学就算了吧,还考语文?强烈谴责出题人这种描述不清题目的行为!!

    • 先对序列按a+b的值排序在进行DP

    • 首先考虑n2的DP,f[i][j]表示考虑前i个元素,a的最大值为j的最大价值

        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                f[i][j] = std::max(f[i][j], f[i-1][j]);
                if (a[i].b >= j) f[i][std::max(j, a[i].a)] = std::max(f[i][std::max(j, a[i].a)], f[i-1][j] + a[i].w);
                ans = std::max(ans, f[i][j]);
            }
        }
    
    • 这样只有60分。考虑优化。

    • 可以每次发现需要修改的值不多,每次只有f[i][a[i]]会被修改最大值,还有f[i][a[i]]~f[i][b[i]]的值会加上w[i],、

    • 就写一颗支持最大值查询,区间加,单点修改的线段树即可

    Code
    #include <cstdio>
    #include <algorithm>
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    
    typedef long long ll;
    const int N = 1e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    struct Node {
        int a, b, w;
        bool operator < (const Node &y) const {
            return a + b < y.a + y.b;
        }
    }a[N];
    int n, b[N<<1], m;
    ll t[N<<3], tag[N<<3];
    
    void Pushdown(int rt) {
        t[ls] += tag[rt];
        t[rs] += tag[rt];
        tag[ls] += tag[rt];
        tag[rs] += tag[rt];
        tag[rt] = 0;
    }
    
    ll Ask(int rt, int l, int r, int x, int y) {
        if (x > r || y < l) return 0;
        if (x <= l && r <= y) return t[rt];
        if (tag[rt]) Pushdown(rt);
        int mid = l + r >> 1;
        return std::max(Ask(ls, l, mid, x, y), Ask(rs, mid + 1, r, x, y));
    }
    
    void Add(int rt, int l, int r, int x, int y, int w) {
        if (x > r || y < l) return;
        if (x <= l && r <= y) return t[rt] += w, tag[rt] += w, void();
        if (tag[rt]) Pushdown(rt);
        int mid = l + r >> 1;
        Add(ls, l, mid, x, y, w);
        Add(rs, mid + 1, r, x, y, w);
        t[rt] = std::max(t[ls], t[rs]);
    }
    
    void Change(int rt, int l, int r, int x, ll w) {
        if (l == r) return t[rt] = std::max(t[rt], w), void();
        if (tag[rt]) Pushdown(rt);
        int mid = l + r >> 1;
        if (x <= mid) Change(ls, l, mid, x, w);
        else Change(rs, mid + 1, r, x, w);
        t[rt] = std::max(t[ls], t[rs]);
    }
    
    int main() {
        freopen("pair.in", "r", stdin);
        freopen("pair.out", "w", stdout);
        n = read();
        for (int i = 1; i <= n; ++i) {
            a[i].a = b[++m] = read();
            a[i].b = b[++m] = read();
            a[i].w = read();
        }
        std::sort(b + 1, b + m + 1);
        m = std::unique(b + 1, b + m + 1) - b - 1;
        for (int i = 1; i <= n; ++i) {
            a[i].a = std::lower_bound(b + 1, b + m + 1, a[i].a) - b;
            a[i].b = std::lower_bound(b + 1, b + m + 1, a[i].b) - b;
        }
        std::sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; ++i) {
            ll mx = Ask(1, 1, m, 1, std::min(a[i].a, a[i].b));
            Add(1, 1, m, a[i].a, a[i].b, a[i].w);
            Change(1, 1, m, a[i].a, mx + a[i].w);
        }
        printf("%lld
    ", t[1]);
        return 0;
    }
    

    C 最小距离

    • 我们的姚队实在是钛聚辣!!我再次用暴力碾过了正解,我跑三千多毫秒,有好几个正解跑了四、五千毫秒

    • 一眼就是原题,除了跑多源最短路没想到,其他的都还有些印象,然后就只能对每个特殊点都跑一遍dij了,没想到A了,交到原来的题上只有15分。

    • 以所有特殊点为起点跑多源最短路,并记录每个点是由那个点更新的,然后枚举每条边,如果两端点的起点不一样,就更新这两个起点的答案。

    Code
    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    typedef long long ll;
    const int N = 2e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    struct Edge {
        int next, t, d;
    }e[N<<1];
    int head[N], edc;
    
    void Add(int x, int y, int z) {
        e[++edc] = (Edge) {head[x], y, z};
        head[x] = edc;
    }
    
    std::priority_queue< std::pair<ll, int> > q;
    int n, m, p, a[N], c[N];
    ll d[N], ans[N];
    bool v[N];
    
    int main() {
        freopen("mindis.in", "r", stdin);
        freopen("mindis.out", "w", stdout);
        n = read(), m = read(); p = read();
        memset(d, 0x3f, 4 * n + 4);
        memset(ans, 0x3f, 4 * n + 4);
        for (int i = 1; i <= p; ++i) {
            a[i] = read();
            c[a[i]] = a[i];
            q.push(std::make_pair(d[a[i]] = 0, a[i]));
        }
        while (m--) {
            int x = read(), y = read(), z = read();
            Add(x, y, z); Add(y, x, z);
        }
        while (!q.empty()) {
            int x = q.top().second; q.pop();
            if (v[x]) continue;
            v[x] = 1;
            for (int i = head[x]; i; i = e[i].next) {
                int y = e[i].t;
                if (d[y] > d[x] + e[i].d) {
                    d[y] = d[x] + e[i].d;
                    c[y] = c[x];
                    q.push(std::make_pair(-d[y], y));
                }
            }
        }
        for (int i = 1; i <= edc; i += 2) {
            int x = e[i].t, y = e[i+1].t;
            ll dis = d[x] + d[y] + e[i].d;
            x = c[x], y = c[y];
            if (x == y) continue;
            ans[x] = std::min(ans[x], dis);
            ans[y] = std::min(ans[y], dis);
        }
        for (int i = 1; i <= p; ++i)
            printf("%lld ", ans[a[i]]);
        return 0;
    }
    

    D 真相 (Unaccepted)

    • 由于T1肝了2小时,导致我实在是没时间了,就rand了一下,拿了0分



    联赛模拟测试22

    阶段排名23

    A 求和

    • 式子很显然,就是等差数列求和,主要是会爆longlong,就得用点别的方法

    • 公式里有除以2,逆元不是很好算,所以就改成先除后龟速乘。


    B 分组配对

    • 本来想二分一下,算了算复杂度(O(n^2log^2n)),就写了个n方暴力,拿了60分

    • 很显然的两个贪心,组内的最大实力值是大的乘大的,小的乘小的,然后能放进组里就放,放不进就开了新组

    • 用倍增优化二分,先判断组长为(2^1,2^2,...)是否满足,到(2^x)时不满足时,就在(2^{x-1}~2^x)区间内二分长度(我二分的是右端点)

    Code

    Show Code
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    const int N = 5e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar())
            if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar())
            x = x * 10 + c - '0';
        return x * f;
    }
    
    int n, ans, a[N], b[N], sa[N], sb[N];
    long long m;
    
    bool Judge(int l, int r) {
        memcpy(sa + l, a + l, 4 * (r - l + 1));
        memcpy(sb + l, b + l, 4 * (r - l + 1));
        std::sort(sa + l, sa + r + 1);
        std::sort(sb + l, sb + r + 1);
        long long s = 0;
        for (int i = l; i <= r; ++i)
            if ((s += 1LL * sa[i] * sb[i]) > m)
                return 0;
        return 1;
    }
    
    int main() {
        freopen("pair.in", "r", stdin);
        freopen("pair.out", "w", stdout);
        n = read();
        scanf("%lld", &m);
        for (int i = 1; i <= n; ++i)
            a[i] = read();
        for (int i = 1; i <= n; ++i)
            b[i] = read();
        int x = 1;
        while (x <= n) {
            int i, l = 0, r;
            for (i = 2; x + i - 1 <= n; i <<= 1) {
                if (Judge(x, x + i - 1)) continue;
                l = x + i / 2 - 1, r = x + i - 1;
                break;
            }
            if (!l) l = x + i / 2 - 1, r = n;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (Judge(x, mid)) l = mid;
                else r = mid - 1;
            }
            x = l + 1;
            ans++;
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    C 城市游戏 (Unaccepted)

    • 看错题了,只拿了15分

    D 简单计算

    • 考场上没仔细想,拿了50分就走了。‘

    • 正解就是考虑向下取整会比原来少多少

    1LL * (n + 1) * m - n + Gcd(n, m) >> 1



    联赛模拟测试21

    阶段排名 28

    A 小盆友的游戏

    • T1就是期望题,直接puts("0"),虽然给了5个样例但还是没发毛线关系

    • 正解是构造函数(f_x=2^x-1),设整个局面的函数值为所有人函数值之和

    • 发现每猜一次拳局面的函数值的变化量都是1

    • 所以猜拳的次数就是最终局面的函数值与开始局面的函数值的差

    B 花

    • 写的暴力实在是太暴力了,于是它T成0分了

    • 定义f[i][0/1]前i个位置的方案数,0表示还没有出现连续3个的情况,1相反。转移方程

    [f_{i, 0}=f_{i-1, 0}*(s-1)+f_{i-2, 0}*(s-1) ]

    [f_{i, 1}=f_{i-1, 1}*(s-1)+f_{i-2, 1}*(s-1)+f_{i-3,0}*(s-1) ]

    C 表格 (Unaccepted)

    • 开始YY了一个50分写法,结果跑的比暴力还慢,最后就只有10分,不过整个机房最高分也就是10分了

    D 格式化

    • 显然需要贪心排序,由于实在是太菜了,就没想出来,写了50分部分分然后用random_shuffle拿了15分。

    • 先放使容量增加的,再放容量不变的,最后放使容量减小的

    • 对于容量增加的,先放格式化之前小的

    • 对于容量减小的,先放格式化之后大的


  • 相关阅读:
    java 数字加密
    java 计算旅途时间
    java练习题3
    java练习题
    java练习
    java练习题
    删除记录模块的设计与实现
    修改记录模块的设计与实现
    提交页面
    采用PreparedStatement的对象实现记录的查询操作
  • 原文地址:https://www.cnblogs.com/shawk/p/13864230.html
Copyright © 2020-2023  润新知