ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

省选测试36

2021-03-05 19:04:05  阅读:139  来源: 互联网

标签:rt Code 省选 中位数 36 int 1ll 测试 ans



T3没删调试,不然有30。后来发现快读写错了,80分没了啊

A Manager

题目大意 : 给出一颗关系树,每人有一个能力值,每人获得的奖金是子树内能力值的中位数(\(\frac{k+1}{2}\)),询问每个人的能力改为1e5之后需要发多少奖金

  • 考场上写的暴力,弄个vector存每个点子树内的所有数,然后sort一下找中位数。一条链就能卡到n2,但数据太水,与是过了(讲完题我就变成95了)

  • 其实还写了线段树合并,只是写的太麻烦,还要区间加打标记,然后没调出来

  • 可以用权值线段树算出中位数,线段树合并,求出每个子树的中位数和中位数下一个数,然后在搜一遍树算答案

  • 算答案就看这个数在祖先里能排到多少,如果在后一半里,变大了也没有,在前一半就变成中位数的下一位了

  • 树状数组可以算出中位数比a[x]打的中位数下一位的和

  • 时间复杂度 \(O(n \log n)\)

Code

Show Code
#include <cstdio>

using namespace std;
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 n, t;
}e[N];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

int n, a[N], fa[N], rt[N], trc, m[N], d[N];
ll sum, ans[N];

struct Tree {
    int sz, l, r;
}t[N*21];

void Insert(int &rt, int l, int r, int x) {
    if (!rt) rt = ++trc; t[rt].sz++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (x <= mid) Insert(t[rt].l, l, mid, x);
    else Insert(t[rt].r, mid + 1, r, x);
}

void Marge(int &x, int y) {
    if (!x) return x = y, void();
    t[x].sz += t[y].sz;
    if (t[y].l) Marge(t[x].l, t[y].l);
    if (t[y].r) Marge(t[x].r, t[y].r);
}

int Find(int rt, int l, int r, int x) {
    if (l == r) return l;
    int mid = l + r >> 1;
    if (x <= t[t[rt].l].sz) return Find(t[rt].l, l, mid, x);
    else return Find(t[rt].r, mid + 1, r, x - t[t[rt].l].sz);
}

struct Tarray {
    ll t[N / 2];

    void Add(int x, int w) {
        for (; x; x -= x & -x) t[x] += w;
    }

    ll Ask(int x, int w = 0) {
        for (; x <= 1e5; x += x & -x) w += t[x];
        return w;
    }
}T;

void Dfs(int x) {
    T.Add(m[x], d[x]);
    ans[x] = T.Ask(a[x]) + sum;
    for (int i = h[x]; i; i = e[i].n) Dfs(e[i].t);
    T.Add(m[x], -d[x]);
}

int main() {
    freopen("manager.in", "r", stdin);
    freopen("manager.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = 2; i <= n; ++i) Add(fa[i] = read(), i);
    for (int i = n; i >= 1; --i) {
        Insert(rt[i], 1, 1e5, a[i]);
        Marge(rt[fa[i]], rt[i]);
        int k = t[rt[i]].sz + 1 >> 1;
        m[i] = Find(rt[i], 1, 1e5, k);
        sum += m[i];
        if (t[rt[i]].sz == 1) d[i] = 1e5 - a[i];
        else d[i] = Find(rt[i], 1, 1e5, k + 1) - m[i];
    }
    Dfs(1);
    for (int i = 1; i <= n; ++i)
        printf("%lld\n", ans[i]);
    return 0;
}

B GCD再放送 (Unaccepted)

题目大意 :

Code

Show Code

C dict

题目大意 : 给出一个不可重集B,和一个排列,让求出比B字典序小的不可重集A的方案数,这里的字典序比较顺序是先比较第p[1]小的,然后比第p[2]小的……

  • 很容易想到枚举A在哪一位第一次比B小,然后前面(p[1]-p[i-1])的位置与B的一样,后面的位置在保证集合性质的大小关系的情况随便选

  • 然后枚举A在这一位是多少,然后枚举A中没选的那些间隙组合数一乘

  • 假如x,y是相邻的确定的位置,那么这中间的方案数就是\(C_{a[y]-a[x]-1}^{y-x-1}\)

  • 这样就是n3的了(假设n与m同阶),发现其实最后一层枚举中除了p[i]前后这两个区间的答案会改变,别的都不变,所以提前算出来就好了,可以优化到n2

  • 这样就有75了,如果在枚举放的是几的时候不枚举显然不合法的,就有80了,考场上如果删了调试,并且把快读写对了就能拿到这分了(话说调试就是因为快读写错了,后来调线段树合并就忘了)

  • 然后发现没放一个p[i]就相当与把1到m这个区间中的一段分成两段,然后扫下面一段,如果每次分的点都是在上面,那么就会跑到n2(相反会O(n)就行)

  • 解决这个的话就可以启发式分裂,因为扫上面得到的答案和扫下面得到的答案加起来就是这一段原本的答案,所以如果上面小的话就可以扫上面,然后那总的减一下就行

  • 时间复杂度 \(O(n \log n)\)

Code

Show Code
#include <set>
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 2e5 + 5, M = 998244353;

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;
}

set<int> s;
int n, m, a[N], p[N], fac[N], inv[N], ans, sum;

int Pow(int a, int k, int ans = 1) {
    for (; k; k >>= 1, a = 1ll * a * a % M)
        if (k & 1) ans = 1ll * ans * a % M;
    return ans;
}

void Init() {
    fac[0] = 1; 
    for (int i = 1; i <= m; ++i)
        fac[i] = 1ll * fac[i-1] * i % M;
    inv[m] = Pow(fac[m], M - 2) % M;
    for (int i = m; i >= 1; --i)
        inv[i-1] = 1ll * inv[i] * i % M;
}

int C(int n, int m) {
    if (n < m) return 0;
    return 1ll * fac[n] * inv[m] % M * inv[n-m] % M;
}

int main() {
    freopen("dict.in", "r", stdin);
    freopen("dict.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i <= n; ++i)
        p[i] = read();
    sort(a + 1, a + n + 1);
    s.insert(0); s.insert(n+1); 
    Init(); a[n+1] = m+1; ans = C(m, n);
    for (int i = 1; i <= n; ++i) {
        set<int>::iterator it = s.lower_bound(p[i]);
        int x, y = p[i], z = *it; x = *--it; s.insert(y);
        int A = y - x - 1, B = z - y - 1, c = C(a[z] - a[x] - 1, z - x - 1);
        ans = 1ll * ans * Pow(c, M - 2) % M;
        if (a[y] - a[x] - 1 <= a[z] - a[y]) {
            for (int j = a[x] + 1; j <= a[y] - 1; ++j)
                if ((sum += 1ll * C(j - a[x] - 1, A) * C(a[z] - j - 1, B) % M * ans % M) >= M) sum -= M;
        }
        else {
            if ((sum += 1ll * ans * c % M) >= M) sum -= M;
            for (int j = a[y]; j <= a[z] - 1; ++j)
                if ((sum -= 1ll * C(j - a[x] - 1, A) * C(a[z] - j - 1, B) % M * ans % M) < 0) sum += M;
        }
        ans = 1ll * C(a[y] - a[x] - 1, y - x - 1) * C(a[z] - a[y] - 1, z - y - 1) % M * ans % M;
    }
    printf("%d\n", sum);
    return 0;
}

标签:rt,Code,省选,中位数,36,int,1ll,测试,ans
来源: https://www.cnblogs.com/shawk/p/14487980.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有