zoukankan      html  css  js  c++  java
  • 2019-08-20 纪中NOIP模拟A组

    T1 [JZOJ6310] Global warming

    题目描述

      给定整数 n 和 x,以及一个大小为 n 的序列 a。

      你可以选择一个区间 [l,r],然后令 a[i]+=d(l<=i<=r),其中 d 满足 |d|<=x。

      要求最大化 a 的最长上升子序列的长度,并输出该值。

    数据范围

      对于 $5\%$ 的数据点,$n,x leq 10$

      对于另外 $10\%$ 的数据点,$n,x leq 50$

      对于另外 $13\%$ 的数据点,$n leq 1000$

      对于另外 $10\%$ 的数据点,$x=0$

      对于另外 $20\%$ 的数据点,$x leq 5$,$n leq 5 imes 10^4$

      对于另外 $17\%$ 的数据点,$x=10^9$

      对于 $100\%$ 的数据点,$n leq 2 imes 10^5$,$x leq 10^9$

    分析

      $Subtask$ 真是让人头大,玄学挂了两个点结果只得了 $62 \, pts$

      这题有几个很显然的地方

      令区间 $[l,r] ; (1 leq l leq r < n)$ 加上 $i ; (0 leq i leq x)$ 必不优于区间 $[l,n]$

      令区间 $[l,r] ; (1 < l leq r leq n)$ 减去 $i ; (0 leq i leq x)$ 必不优于区间 $[1,r]$

      令区间 $[l,n]$ 加上 $i ; (0 leq i < x)$ 或令区间 $[1,r]$ 减去 $i$ 必不优于加上/减去 $x$

      由于加减实际上是等价的,所以我们假定让区间 $[1,r] ; (1 leq r < n)$ 减去 $x$

      设 $f_i$ 表示减小的区间为 $[1,i-1]$ 且 $a_i$ 被选中时的最长上升子序列长度

      首先正序做一遍最长上升子序列可以处理出 $f$ 数组,然后倒序做一遍找出最优解

      若当前位置为 $i$,则 $ans_i=f_i \, +$ 以 $i$ 结尾的最长下降子序列长度(倒序)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib> 
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 200005
    
    int n, x, pos, ans;
    int a[N], q[N], f[N];
    
    int main() {
        scanf("%d%d", &n, &x);
        for (int i = 1; i <= n; i++) scanf("%d", a + i);
        for (int i = 1; i <= n; i++) {
            f[i] = lower_bound(q + 1, q + q[0] + 1, a[i]) - q - 1;
            pos = lower_bound(q + 1, q + q[0] + 1, a[i] - x) - q;
            q[pos] = a[i] - x; q[0] = max(q[0], pos);
        }
        q[0] = 0;
        for (int i = n; i; i--) {
            pos = lower_bound(q + 1, q + q[0] + 1, a[i], greater<int>()) - q;
            q[pos] = a[i]; q[0] = max(q[0], pos);
            ans = max(ans, f[i] + pos);
        }
        printf("%d", ans);
        
        return 0;
    }
    View Code

    T2 [JZOJ6311] Mobitel

    题目描述

      给定一个 r 行 s 列的矩阵,每个格子里都有一个正整数。

      问如果从左上角走到右下角,且每次只能向右或向下走到相邻格子,那么使得路径上所有数的乘积不小于 n 的路径有多少条?

      由于答案可能很大,所以请输出答案对 10^9+7 取模的结果。

    数据范围

      对于 $20\%$ 的数据,矩阵中的数不超过 $10$

      对于 $50\%$ 的数据,$1 leq r,s leq 100$

      对于 $100\%$ 的数据,$1 leq r,s leq 300$,$1 leq n leq 10^6$,矩阵中的数不超过 $10^6$

    分析

      显然可以先求出总路径数,再减去乘积小于 $n$ 的路径数得到答案

      刚开始很容易想到设 $f[i][j][k]$ 表示走到点 $(i,j)$ 处乘积为 $k$ 时的路径数

      但时间空间都不允许 $O(rsn)$ 的复杂度

      考虑优化状态 $k$,我们发现可以保存剩下还可以乘的数的状态

      即 $f[i][j][k]$ 表示走到点 $(i,j)$ 处乘积 $x$ 满足 $lfloor frac{n-1}{x} floor =k$ 的路径数

      此时 $k$ 的种类数为 $2 sqrt{n}$,总时间复杂度为 $O(rs sqrt{n})$

    #include <iostream>
    #include <cstdio>
    #include <cstdlib> 
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 305
    #define M 2005
    
    const int p = 1e9 + 7;
    int r, c, n, m, q, sum;
    int g[N][N], f[2][N][M];
    int inv[N], w[M], id[1000005];
    
    int Sum() {
        ll ans = 1; inv[1] = 1;
        for (int i = r; i <= r + c - 2; i++)
            ans = ans * i % p;
        for (int i = 2; i < c; i++) {
            inv[i] = (ll)(p - p / i) * inv[p % i] % p;
            ans = ans * inv[i] % p;
        }
        return (int)ans;
    }
    
    int main() {
        scanf("%d%d%d", &r, &c, &n); n--;
        for (int x = 1, y; x <= n; x = y + 1)
            y = n / (n / x), w[++m] = n / x, id[w[m]] = m;
        f[0][1][1] = 1;
        for (int i = 1; i <= r; i++, q ^= 1) {
            memset(f[q ^ 1], 0, sizeof f[q ^ 1]);
            for (int j = 1; j <= c; j++) {
                int x; scanf("%d", &x);
                for (int k = 1; k <= m; k++)
                    f[q ^ 1][j][id[w[k] / x]] = (f[q ^ 1][j][id[w[k] / x]] +
                        (f[q][j][k] + f[q ^ 1][j - 1][k]) % p) % p;
            }
        }
        for (int i = 1; i <= m; i++)
            sum = (sum + f[q][c][i]) % p;
        printf("%d", (Sum() - sum + p) % p);
        
        return 0;
    }
    View Code

    T3 [JZOJ6312] Lottery

    题目描述

      定义两个序列对应位置上不同的值的个数不超过 k,则可称为 k 相似。

      现在有一个长度为 n 的序列 a,将它划分为 n−l+1 个长度为 l 的子串(第 i 个子串为 a[i]~a[i+l-1])

      有 q 组询问,第 j 组询问给出一个 kj,求每个子串与多少个其它的子串可称为 kj 相似。

    数据范围

      对于 $25\%$ 的数据点,$n leq 300$

      对于另外 $20\%$ 的数据点,$n leq 2 imes 10^3$

      对于另外 $20\%$ 的数据点,$q=1$,$k_1=1$

      对于另外 $15\%$ 的数据点,$q=1$

      对于 $100\%$ 的数据点,$k_j leq l leq n leq 10^4$,$q leq 100$,$a_i leq 10^9$

    分析

      只要常数小,$n$ 方过一万有时也能成为正解 ——讲题人

      如果直接暴力判断相似,那么时间复杂度为 $O(n^2l)$

      实际上如果我们已知 $[l_1,r_1]$ 和 $[l_2,r_2]$ 的相似度,那么就可以 $O(1)$ 求出 $[l_1+1,r_1+1]$ 和 $[l_2+1,r_2+1]$ 相似度

      这样判断子串之间相似的复杂度为 $O(n^2)$

      如果我们用 $f[i][j]$ 表示 $i$ 串与 $j$ 串的不相似度,那么在每个子串输出答案时还需要遍历一遍其他所有子串,则输出时的时间复杂度为 $O(n^2q)$

      所以可以用 $f[i][k]$ 表示与 $i$ 串不相似度为 $k$ 的子串数,但此时空间复杂度依然无法接受

      我们发现询问次数非常小,所以数组第二维只需要开到 $q$,最后再用前缀和计算一下

      总时间复杂度为 $O(n^2)$,空间复杂度为 $O(nq)$

    #include <iostream>
    #include <cstdio>
    #include <cstdlib> 
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 10005
    #define M 105
    
    int n, m, l, k;
    int a[N], b[M], f[N][M], pos[N];
    
    struct Query {int val, id, num;} q[M];
    bool cmp1(Query x, Query y) {return x.val < y.val;}
    bool cmp2(Query x, Query y) {return x.id < y.id;}
    
    int main() {
        scanf("%d%d", &n, &l);
        for (int i = 1; i <= n; i++)
            scanf("%d", a + i);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++)
            scanf("%d", &q[i].val), q[i].id = i;
        sort(q + 1, q + m + 1, cmp1);
        for (int i = 1; i <= m; i++)
            q[i].num = i, b[i] = q[i].val;
        for (int i = 0; i <= l; i++)
            pos[i] = lower_bound(b + 1, b + m + 1, i) - b;
        for (int i = 1; i + l - 1 < n; i++, k = 0) {
            for (int j = 1; j <= l; j++)
                if (a[j] != a[j + i]) k++;
            int p = pos[k]; f[1][p]++; f[1 + i][p]++;
            for (int j = 2; j + i + l - 1 <= n; j++) {
                k += (a[j + l - 1] != a[j + i + l - 1]) - (a[j - 1] != a[j + i - 1]);
                p = pos[k]; f[j][p]++; f[j + i][p]++;
            }
        }
        for (int i = 1; i <= n - l + 1; i++)
            for (int j = 1; j <= m; j++)
                f[i][j] += f[i][j - 1];
        sort(q + 1, q + m + 1, cmp2);
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n - l + 1; j++)
                printf("%d ", f[j][q[i].num]);
            printf("
    ");
        }
        
        return 0;
    }
    View Code
  • 相关阅读:
    null in ABAP and nullpointer in Java
    SAP ABAP SM50事务码和Hybris Commerce的线程管理器
    Hybris service layer和SAP CRM WebClient UI架构的横向比较
    SAP ABAP和Linux系统里如何检查网络传输的数据量
    SAP CRM WebClient UI和Hybris的controller是如何被调用的
    SAP CRM和Cloud for Customer订单中的业务伙伴的自动决定机制
    SAP CRM WebClient UI和Hybris CommerceUI tag的渲染逻辑
    SAP BSP和JSP页面里UI元素的ID生成逻辑
    微信jsapi支付
    微信jsapi退款操作
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11385084.html
Copyright © 2011-2022 走看看