联赛前第七阶段总结

和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分。

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

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

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


原文地址:https://www.cnblogs.com/shawk/p/13864230.html