zoukankan      html  css  js  c++  java
  • 牛客多校Round 5

    Solved:3

    rank:195

    F. take

    官方题解:小 A 在打开第 i 个箱子后会交换手中的钻石和第 i 个箱子中的钻石

    当且仅当第 i个箱子的钻石是前 i 个箱子打开后出现的所有钻石里最大的。

    那么要算概率的话,前面箱子中钻石大于等于它的打开后就不能有钻石
    用树状数组维护一下

    线段树(不会树状数组) 调了半天居然快速幂忘记写返回了

    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    using namespace std;
    typedef long long ll;
    const ll mod = 998244353;
    
    ll sum[400005];
    int num[100005];
    
    ll pow_mod(ll x, ll y)
    {
        ll res = 1;
        while(y)
        {
            if(y & 1) res = res * x % mod;
            y >>= 1;
            x = x * x % mod; 
        }
        return res;
    }
    
    struct node
    {
        int p, d, id;
    }E[100005];
    
    bool cmp1(node A, node B)
    {
        return A.d < B.d;    
    } 
    
    bool cmp2(node A, node B)
    {
        return A.id < B.id;
    }
    
    void pushup(int rt)
    {
        sum[rt] = sum[rt << 1] * sum[rt << 1 | 1] % mod;
    }
    
    void build(int l, int r, int rt)
    {
        if(l == r)
        {
            sum[rt] = 1;
            return;
        }
        
        int m = l + r >> 1;
        build(l, m, rt << 1);
        build(m + 1, r , rt << 1 | 1);
        pushup(rt);
    }
    
    void update(int k, ll v, int l, int r, int rt)
    {
        if(l == r)
        {
            sum[rt] = v;
            return;
        }
        
        int m = l + r >> 1;
        if(k <= m) update(k, v, l, m, rt << 1);
        else update(k, v, m + 1, r, rt << 1 | 1);
        pushup(rt);
    }
    
    ll query(int ql, int qr, int l, int r, int rt)
    {
        if(ql <= l && qr >= r) return sum[rt];
        
        ll res = 1;
        int m = l + r >> 1;
        if(ql <= m) res = res * query(ql, qr, l, m, rt << 1) % mod;
        if(qr > m) res = res * query(ql, qr, m + 1, r, rt << 1 | 1) % mod;
        return res;
    }
    
    int main()
    {
        int n;
        scanf("%d", &n);
        ll ny = pow_mod(100, mod - 2);    
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d", &E[i].p, &E[i].d);
            E[i].id = i;
            E[i].p = 100 - E[i].p;
        }
        sort(E + 1, E + 1 + n, cmp1);
        for(int i = 1; i <= n; i++) num[E[i].id] = i;
        sort(E + 1, E + 1 + n, cmp2);
        build(1, n, 1);
        
        ll ans = 0;
        for(int i = 1; i <= n; i++)
        {
            int v = num[E[i].id]; 
            ans = (ans + query(v, n, 1, n, 1) * (100 - E[i].p) % mod * ny % mod) % mod;
            update(v, E[i].p * ny % mod, 1, n, 1); 
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    H. subseq

    题意:找一个字典序第k小的合法b数组 使得以b数组的大小作为下标的a数组是严格上升的子序列

    题解:用f(i)表示从第i个元素做为起始能组成的上升子序列个数

    跃然纸上的转移 f(i) = 1 + ∑f(j) (i < j <= n && a(j) > a(i)) 这个用树状数组维护就好了

    离散好以后 从大到小的插入就可以

    然后贪心的从1枚举 在满足这个点a(i)大于上一个插入答案中的点的情况下

    如果f(i) >= k就意味着这个点可以做为答案中的一位 k-- 就少了后面什么都不接的一种

    如果f(i) < k 表示这个点显然不做为答案中的一位 k -= f[i] 把这些字典序比他小的答案全减去

    还有个很坑的地方就是可能会爆ll 但是问的k只有1e18 所以判一下上界 用我的写法用后缀和比较好

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int n;
    ll k;
    struct node
    {
        int val, id, v;
    }a[500005];
    ll c[500005];
    ll f[500005];
    int ans[500005];
    
    bool cmp(node A, node B) {return A.val < B.val;}
    bool cmp1(node A, node B) {return A.id < B.id;}
    inline void add(int x, ll v)
    {
        for( ; x > 0; x -= x & -x)
        {
            c[x] += v;
            if(c[x] > 1e18) c[x] = 1e18;
        }
    }
    inline ll ask(int x)
    {
        ll res = 0;
        for( ; x <= n; x += x & -x)
        {
            res += c[x];
            if(res > 1e18) res = 1e18;
        }
        return res;
    }
    
    int main()
    {
        scanf("%d%lld", &n, &k);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i].val), a[i].v = a[i].val, a[i].id = i;
        sort(a + 1, a + 1 + n, cmp);
    
        a[1].val = 1;
        for(int i = 2; i <= n; i++)
        {
            if(a[i].val == a[i - 1].v) a[i].val = a[i - 1].val;
            else a[i].val = a[i - 1].val + 1;
        }
        sort(a + 1, a + 1 + n, cmp1);
    
        for(int i = n; i >= 1; i--)
        {
            //f[i] = ask(n) - ask(a[i].val * 1LL) + 1LL;
            //这样写会wa 因为两个都很大的情况下 返回1e18 但是一减去就没了 
            f[i] = ask(a[i].val + 1) + 1;
            add(a[i].val, f[i]);
        }
    
        int cnt = 0;
        for(int i = 1; i <= n; i++)
        {
            if(!k) break;
            if(a[i].val > a[ans[cnt]].val)
            {
                if(f[i] >= k) ans[++cnt] = i, k--;
                else k -= f[i];
            }
        }
        if(k) puts("-1");
        else
        {
            printf("%d
    ", cnt);
            for(int i = 1; i < cnt; i++) printf("%d ", ans[i]);
            printf("%d
    ", ans[cnt]);
        }
        return 0;
    }
    View Code

    I. vcd

    题意:n个点 定义一个集合是好的为 对于这个集合的任意一个子集 以右开口的矩形把他包起来 不会包含这个子集的其他点

       求有多少个好的集合

    题解:考虑一个点的话显然都满足

    考虑两个点的话显然纵坐标相同的不满足情况 那么统计答案就减去纵坐标相同的

    考虑三个点的话显然是满足像小于符号的点才行(好像不是很显然 但是多画几种情况就看出来了)

    统计答案就是对于每个点 在比这个点横坐标大的情况下 在他上面的点 x 在他下面的点 且任意两个点横坐标不能相同

    所以我们用树状数组维护y 再按x从大到小的插入 对于相同的x 都统计完答案后再一起插入

    考虑四个点的话是找不到答案的 简单的证明一下 对于一个像红对勾形状的三个点

    假设再加入一个点要满足答案 那么新加入的点和任意两个点都能组成小于号

    对于右边两个点 要组成小于号 要在下面哪个点下面

    对于左边两个点 要组成小于号 要在上面哪个点上面 这显然是矛盾的

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod = 998244353;
    
    ll n;
    struct node
    {
        int x, y, pre;
    }p[100005];
    int b[100005];
    ll vis[100005];
    ll c[100005];
    
    bool cmp1(node A, node B) {return A.y < B.y;}
    bool cmp2(node A, node B) {return A.x < B.x;}
    inline void add(int x) {for( ; x <= n; x += x & -x) c[x]++;}
    inline ll ask(int x) {ll res = 0; for( ; x > 0; x -= x & -x) res += c[x], res %= mod; return res;}
    
    int main()
    {
        scanf("%lld", &n);
        for(int i = 1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y), p[i].pre = p[i].y;
        sort(p + 1, p + 1 + n, cmp1);
    
        p[1].y = 1; vis[1]++; //这沙雕地方卡了一晚上 写昏了
        for(int i = 2; i <= n; i++)
        {
            if(p[i].y == p[i - 1].pre) p[i].y = p[i - 1].y;
            else p[i].y = p[i - 1].y + 1;
            vis[p[i].y]++;
        }
        sort(p + 1, p + 1 + n, cmp2);
    
        ll ans = 0;
        ans += 1LL * n + n * (n - 1LL) / 2LL;
        for(int i = 1; i <= n; i++) ans -= vis[i] * (vis[i] - 1) / 2;
        ans %= mod;
    
        int now = n;
        for(int i = n; i >= 1; i--)
        {
            ll tmp1 = ask(n) - ask(p[i].y);
            ll tmp2 = ask(p[i].y - 1);
            ans += tmp1 * tmp2 % mod;
            ans %= mod;
    
            if(p[i].x != p[i - 1].x)
            {
                for(int j = now; j >= i; j--) add(p[j].y);
                now = i - 1;
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    二级指针与二维数组
    二维数组的两种访问方式
    函数返回局部变量
    函数指针
    链表
    二叉树各种遍历
    二叉树常见问题
    C语言单链表实现19个功能完全详解
    halcon算子翻译——set_fuzzy_measure_norm_pair
    Halcon算子翻译——set_fuzzy_measure
  • 原文地址:https://www.cnblogs.com/lwqq3/p/9415873.html
Copyright © 2011-2022 走看看