• 2019-08-13 纪中NOIP模拟B组


    T1 [JZOJ1534] rank

    题目描述

      小h和小R正在看之前的期末&三校联考成绩,小R看完成绩之后很伤心,共有n个学生,第i个学生有一个总成绩Xi,因为他的排名是倒数第k个,于是小R想知道那些成绩比他低(包括成绩和他一样)的同学的成绩,这样能让他没那么伤心。

    数据范围

      $1 leq N leq 5 imes 10^6$,$0 leq X leq 10^5$

    分析

      我以为我拿到了C组题,惊了

      数据量大数据值小,显然桶排序

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 5000005
    #define M 100005
    
    int n, k, x, mins = inf, maxs, s[M];
    
    int main() {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x); s[x]++;
            mins = min(mins, x);
            maxs = max(maxs, x);
        }
        int now = 0;
        for (int i = mins; i <= maxs; i++) {
            while (s[i]--) {
                if (++now > k) break;
                printf("%d ", i);
            }
            if (now >= k) break;
        }
        
        return 0;
    }
    View Code

    T2 [JZOJ1536] seek

    题目描述

      俗话说“好命不如好名”,小h准备给他的宠物狗起个新的名字,于是他把一些英文的名字全抄下来了,写成一行长长的字符串,小h觉得一个名字如果是好名字,那么这个名字在这个串中既是前缀,又是后缀,即是这个名字从前面开始可以匹配,从后面开始也可以匹配,例如abc在abcddabc中既是前缀,也是后缀,而ab就不是,可是长长的字符让小h几乎昏过去了,为了给自己的小狗起个好名字,小h向你求救,并且他要求要将所有的好名字的长度都输出来。

    数据范围

      $1 leq vert S vert leq 4 imes 10^5$

    分析

      这题主要是考对 $KMP$ 算法的理解

      事实上这里只需要用到 $KMP$ 中的 $nxt$ 数组,即字符串每个前缀区间的前后缀最长匹配(不包含自身)

      首先原字符串肯定是符合题意的,而对于每个合法的区间,它的最长匹配前缀区间也是合法的

      所以只需要在求出 $nxt$ 数组后,依次输出所有合法区间就可以了

      当然这题用 $Hash$ 来做也是很简单的

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 400005
    
    char s[N];
    int nxt[N];
    
    void find(int x) {
        if (!x) return;
        find(nxt[x]);
        printf("%d ", x);
    }
    
    int main() {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for (int i = 2, j = 0; i <= n; i++) {
            while (j && s[j + 1] != s[i]) j = nxt[j];
            if (s[j + 1] == s[i]) j++;
            nxt[i] = j;
        }
        find(n);
        
        return 0;
    }
    View Code

    T3 [JZOJ1537] pot

    题目描述

      这个假期,小h在自家院子里种了许多花,它们围成了一个圈,从1..n编号,小h对每盆花都有一个喜好值xi,小h现在觉得这样一成不变很枯燥,于是他做了m个改动,每次把第ki盘花改成喜好值为di的花,然后小h要你告诉他,在这个花圈中,连续的最大喜好值是多少(不能整圈都选)。

    数据范围

      $1 leq N,M leq 10^5$,$-10^3 leq X leq 10^3$

    分析

      如果花盆是一条链的形状,那么这题就是一个很裸的线段树维护最大子区间和

      但是这是一个环,所以区间在左右端点处可能是连接着的

      我们发现穿过左右端点的最大子区间和,可以用区间和减去最小子区间来表示

      因此我们只需要再维护最小子区间和,最后在两者中取较大值

      还要注意不能整圈都取,即序列不含 $0$ 时最优解不能等于总区间和

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 100005
    #define lc (p << 1)
    #define rc ((p << 1) | 1)
    #define mid ((l + r) >> 1)
    
    int n, m, k, d, ans, zero;
    
    struct Tree {
        int sum, la, ra, ma, li, ri, mi;
    } t[N << 2];
    
    void pushup(int p) {
        t[p].sum = t[lc].sum + t[rc].sum;
        t[p].la = max(t[lc].la, t[lc].sum + t[rc].la);
        t[p].ra = max(t[rc].ra, t[rc].sum + t[lc].ra);
        t[p].ma = max(t[lc].ra + t[rc].la, max(t[lc].ma, t[rc].ma));
        t[p].li = min(t[lc].li, t[lc].sum + t[rc].li);
        t[p].ri = min(t[rc].ri, t[rc].sum + t[lc].ri);
        t[p].mi = min(t[lc].ri + t[rc].li, min(t[lc].mi, t[rc].mi));
    }
    
    void build(int p, int l, int r) {
        if (l == r) {
            scanf("%d", &t[p].sum);
            t[p].la = t[p].ra = t[p].ma = t[p].sum;
            t[p].li = t[p].ri = t[p].mi = t[p].sum;
            if (!t[p].sum) zero++;
            return;
        }
        build(lc, l, mid);
        build(rc, mid + 1, r);
        pushup(p);
    }
    
    void update(int p, int l, int r, int x, int q) {
        if (l > x || r < x) return;
        if (l == r) {
            if (!t[p].sum && q) zero--;
            if (t[p].sum && !q) zero++;
            t[p].sum = q;
            t[p].la = t[p].ra = t[p].ma = t[p].sum;
            t[p].li = t[p].ri = t[p].mi = t[p].sum;
            return;
        }
        update(lc, l, mid, x, q);
        update(rc, mid + 1, r, x, q);
        pushup(p);
    }
    
    int main() {
        scanf("%d", &n);
        build(1, 1, n);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &k, &d);
            update(1, 1, n, k, d);
            if (t[1].ma == t[1].sum && !zero) ans = t[1].sum - t[1].mi;
            else if (t[1].mi == t[1].sum && !zero) ans = t[1].ma;
            else ans = max(t[1].ma, t[1].sum - t[1].mi);
            printf("%d
    ", ans);
        }
        
        return 0;
    }
    View Code

    T4 [JZOJ3512] show

    题目描述

      有三支队伍,分别是A,B,C。有n个游戏节目,玩第i个游戏,队伍A可以得到的分数是A[i],队伍B可以得到的分数是B[i],队伍C可以得到的分数是C[i]。由于时间有限,可能不是每个节目都能玩,于是节目主持人决定要从n个游戏节目里面挑选至少k个节目出来(被选中的节目不分次序),使得队伍A成为赢家。队伍A能成为赢家的条件是队伍A的总得分要比队伍B的总得分要高,同时也要比队伍C的总得分要高。节目主持人有多少种不同的选取方案?

    数据范围

      对于 $40\%$ 的数据,$2 leq N leq 20$

      对于 $100\%$ 的数据,$2 leq N leq 34$,$1 leq K leq min(N,7)$,$1 leq A,B,C leq 10^9$

    分析

      防AK题,当然是选择秒打暴力啊

      仔细观察,会发现这题的数据范围很诡异,状压显然是不行的,又好像没什么方法可以剪枝

      但是,题目中给了一个很小的 $k$,我们会想到用总合法方案数减去不满足 $k$ 限制的合法方案数得到答案

      在不满足 $k$ 的限制范围中,枚举的方案数量最多是 $sumlimits_{i=0}^6 inom{34}{i}$,即 $1676116$ 种,这是不会超时的

      然后在求总合法方案数时,直接搜索时肯定过不了的,我们可以把前一半节目和后一半节目分为两部分

      对于每个部分,枚举的方案数最多为 $2^{17}$,我们可以求出两个部分中每种方案得到的一对值 $(ab_1,ac_1)$,$(ab_2,ac_2)$,其中 $ab$ 为 $A$ 组总得分减去 $B$ 组总得分,$ac$ 为 $A$ 组总得分减去 $C$ 组总得分

      显然,一种完整方案要成立,必须同时满足 $ab_1+ab_2>0$ 和 $ac_1+ac_2>0$

      所以我们可以枚举第一部分中的所有方案,在第二部分中找出与之匹配能够成立的方案

      当然这样就需要用树状数组/线段树之类的数据结构来保存第二部分中的所有方案,同时还需要离散化/动态开点

      时间复杂度为 $O(2^{17} \, log ; 2^{17}+1676116) doteq O(1807193)$

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 40
    #define M (1 << 17) + 5
    
    int n, k, t1, t2, t, tr[M << 1];
    ll ansk, ans, a[N], b[N], c[N];
    
    struct Data {ll ab, ac;} d1[M], d2[M];
    struct Node {ll x, y; int mark;} d[M << 1];
    
    bool cmp1(Data x, Data y) {return x.ac < y.ac;}
    bool cmp2(Data x, Data y) {return x.ac > y.ac;}
    bool cmp(Node x, Node y) {return x.x < y.x;};
    
    void dfsk(int x, int sum, ll sa, ll sb, ll sc) {
        if (sum == k) return;
        if (sa > sb && sa > sc) ansk++;
        for (int i = x + 1; i <= n; i++)
            dfsk(i, sum + 1, sa + a[i], sb + b[i], sc + c[i]);  
    }
    
    void dfs1(int x, ll sa, ll sb, ll sc) {
        d1[++t1].ab = sa - sb, d1[t1].ac = sa - sc;
        for (int i = x + 1; i <= (n >> 1); i++)
            dfs1(i, sa + a[i], sb + b[i], sc + c[i]);
    }
    
    void dfs2(int x, ll sa, ll sb, ll sc) {
        d2[++t2].ab = sa - sb, d2[t2].ac = sa - sc;
        for (int i = x + 1; i <= n; i++)
            dfs2(i, sa + a[i], sb + b[i], sc + c[i]);
    }
    
    int lowbit(int x) {return x & -x;}
    
    void update(int x) {
        while (x <= t) tr[x]++, x += lowbit(x);
    }
    
    int query(int x) {
        int sum = 0;
        while (x) sum += tr[x], x -= lowbit(x);
        return sum;
    }
    
    int main() {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) scanf("%lld", a + i);
        for (int i = 1; i <= n; i++) scanf("%lld", b + i);
        for (int i = 1; i <= n; i++) scanf("%lld", c + i);
        dfsk(0, 0, 0, 0, 0); dfs1(0, 0, 0, 0); dfs2(n >> 1, 0, 0, 0);
        for (int i = 1; i <= t1; i++)
            d[i].x = d1[i].ab, d[i].y = i, d[i].mark = 0;
        for (int i = 1; i <= t2; i++)
            d[t1 + i].x = -d2[i].ab, d[t1 + i].y = i, d[t1 + i].mark = 1;
        sort(d + 1, d + t1 + t2 + 1, cmp);
        d[0].x = -40000000000;
        for (int i = 1; i <= t1 + t2; i++) {
            if (d[i].x != d[i - 1].x) t++;
            if (!d[i].mark) d1[d[i].y].ab = t;
            else d2[d[i].y].ab = t;
        }
        sort(d1 + 1, d1 + t1 + 1, cmp1);
        sort(d2 + 1, d2 + t2 + 1, cmp2);
        for (int i = 1, j = 1; i <= t1; i++) {
            while (j <= t2 && d1[i].ac + d2[j].ac > 0)
                update(d2[j].ab), j++;
            ans += query(d1[i].ab - 1);
        }
        printf("%lld
    ", ans - ansk);
        
        return 0;
    }
    View Code
  • 相关阅读:
    vue2.0动态添加组件
    Kali Linux信息收集工具全
    Kali Linux 弱点分析工具全集
    如何DIY一个简单的反弹Shell脚本
    深入理解DIP、IoC、DI以及IoC容器
    Intellij IDEA常用配置详解
    GIT 的常规操作
    Nodejs学习笔记(一)--- 简介及安装Node.js开发环境
    什么是“对用户友好”
    Facebook为什么使用PHP编程语言?
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11347804.html
Copyright © 2020-2023  润新知