zoukankan      html  css  js  c++  java
  • 省选测试14

    省选测试14

    T1

    你有一个字符串 (S_0)​,想要用它造出共 n 个新字符串 (S_{0},cdots,S_{n-1}​)

    你会按顺序进行 n-1 次操作,造出新字符串。第 i 次操作有两种方法:

    S x l r 截取 (S_x)​ 的 [l,r) 子串,即第 l 到第 r-1 个字符作为 (S_i)

    A x y 将 (S_x)(S_y) 拼接作为 (S_i)


    最后,你会关心 (S_{n-1})​ 长什么样,因此请输出 (S_{n-1})​ 所有字符的 ASCII 码之和,结果对 (10^9+7)取模。

    本题中字符串下标均从 0 开始编号,且保证所有字符串串长不超过 (2^{63}-1),并且所有操作均合法(即(0leq x,y<i,0leq l < rleq Len(S_x))

    对于全部数据,(nleq 2000, 1leq Len(S_0)leq 2000),同时满足题目描述中给的限制。

    搜索 + 记忆化.

    md传参类型传错了少了40pts...

    直接分情况搜索就好了, 记录一个第一个串的前缀和.

    不过我写的记搜比较慢, 我只记录了一个第(i)个字符串的整个的价值是多少, 但是题解记录的是第(i)个字符串前(l)个字符的价值是多少, 可能用的空间大了点(但这题不卡空间).

    #include <bits/stdc++.h>
    
    #define ull unsigned long long
    
    using namespace std;
    
    inline ull read() {
        ull s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 2005, mod = 1e9 + 7;
    int n;
    int sum[N], ans[N];
    ull len[N];
    char ch[N];
    struct ques { int opt, x; ull l, r; } q[N];
    
    int calc_qj(int x, ull l, ull r) {
        if(x == 0) return (sum[r] - sum[l - 1] + mod) % mod;
        if(r - l + 1 == len[x]) return ans[x];
        if(q[x].opt == 2) {
            if(r <= len[q[x].l]) return calc_qj(q[x].l, l, r);
            else if(l > len[q[x].l]) return calc_qj(q[x].r, l - len[q[x].l], r - len[q[x].l]);
            else return (calc_qj(q[x].l, l, len[q[x].l]) + calc_qj(q[x].r, 1, r - len[q[x].l])) % mod;
        }
        if(q[x].opt == 1) {
            return calc_qj(q[x].x, l + q[x].l - 1, r + q[x].l - 1);
        }
    }
    
    int calc(int x) {
        if(ans[x]) return ans[x];
        // cout << x << " " << q[x].l << " " << q[x].r << "
    ";
        if(q[x].opt == 2) {
            ans[x] = (calc(q[x].l) + calc(q[x].r)) % mod;
            len[x] = len[q[x].l] + len[q[x].r];
        }
        else {
            ans[x] = calc_qj(q[x].x, q[x].l, q[x].r);
            len[x] = q[x].r - q[x].l + 1;
            // cout << x << ":" << q[x].r << " " << q[x].l << "
    ";
        }
        return ans[x];
    }
    
    int main() {
    
        // freopen("string.in","r",stdin); freopen("string.out","w",stdout);
    
        scanf("%d", &n);
        scanf("%s", ch + 1);
        int len_ch = strlen(ch + 1);
        for(int i = 1;i <= len_ch; i++) sum[i] = (sum[i - 1] + ch[i]) % mod;
        ans[0] = sum[len_ch]; len[0] = len_ch;
        // for(int i = 1;i <= len_ch; i++) cout << i << ":" << sum[i] << "
    ";
        for(int i = 1;i < n; i++) {
            char ch_; cin >> ch_;
            if(ch_ == 'S') {
                q[i].opt = 1; q[i].x = read(); q[i].l = read() + 1; q[i].r = read();
            }
            if(ch_ == 'A') {
                q[i].opt = 2; q[i].l = read(); q[i].r = read();
            }
        }
        for(int i = 1;i < n - 1; i++) {
            calc(i);
            // cout << i << ":" << ans[i] << " " << len[i] << "
    ";
        }
        printf("%d
    ", calc(n - 1));
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    

    T2

    你有 n 株植物,高度为 (a_i)​。你希望让它们的高度看起来不太相同,具体来说:任意两株高度差的绝对值不小于 d。

    每次你可以选择一株植物,将其拔高或压低 1,这花费你 1 的力气,这个操作可以进行任意多次。注意,植物高度不能是负的,即你需要保证任意时刻 (a_igeq 0)

    你最少需要使用多少力气,才能使得植物高度看起来不太相同呢?

    多组测试数据。第一行一个整数 T 为数据组数,接下来每组格式如下:

    第一行两个整数 n,d。

    接下来一行 n 个整数 (a_i)​。

    对于全部数据,(sum nleq 3cdot 10^5,1leq dleq 10^6, 0leq a_ileq 10^{11})

    开始想了了费用流的做法, 骗了60pts.

    就是先给每个(a_i)排序, 然后求出所有 (x = a_{i+1} - a_i - d), 对于(x)小于0的就从源点向他连一条流量为(abs(x)), 费用为0的边, 对于大于0的就从他向汇点连一条流量为(x), 费用为0的边. 然后这些点之间都连双向的流量为(inf), 费用为1的边. 为啥呢?

    如果说提高一个(i)的高度的话, (i-1)(i)的高度差就会增加1, (i+1)(i)的高度差就会减小1, 然后又花了1的体力. 都减(d)是因为要保证相邻两个的差值大于等于d.

    但是还有特殊的两个节点, 就是最小的那个可以压到0, 最大的那个可以无限高, 于是再连两条特殊的边就好了 : 从0向汇点连一条流量为(a_1), 费用为0的边, 从(n+1)向汇点连一条流量为(inf), 费用为0的边.

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 4e5, M = 3e6;
    const long long inf = 1e18, Inf = 1e9;
    int s, t, n, d_, cnt;
    int d[N], pre[N], las[N], head[N];
    long long b[N], a[N], incf[N];
    struct edge { int v, to, nxt; long long c; } e[M];
    
    void add(int x, int y, long long z1, int z2) {
        // cout << x << " " << y << " " << z1 << " " << z2 << "!!!!
    ";
        e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].c = z1; e[cnt].v = z2;
        e[++ cnt].nxt = head[y]; head[y] = cnt; e[cnt].to = x; e[cnt].c = 0; e[cnt].v = -z2;
    }
    
    int bfs() {
        for(int i = s;i <= t; i++) d[i] = Inf, incf[i] = inf;
        queue <int> q; d[s] = 0; pre[t] = -1; q.push(s);
        while(q.size()) {
            int x = q.front(); q.pop();
            for(int i = head[x]; i ; i = e[i].nxt) {
                int y = e[i].to;
                if(e[i].c && d[y] > d[x] + e[i].v) {
                    d[y] = d[x] + e[i].v; q.push(y);
                    incf[y] = min(incf[x], e[i].c);
                    pre[y] = x; las[y] = i;
                }
            }
        }
        return pre[t] != -1;
    }
    
    void Work() {
        long long res = 0;
        while(bfs()) {
            int now = t;
            res += 1ll * d[t] * incf[t];
            // cout << d[t] << " " << incf[t] << "!!!!!
    ";
            while(now != s) {
                e[las[now]].c -= incf[t];
                e[las[now] ^ 1].c += incf[t];
                now = pre[now];
            }
        }
        printf("%lld
    ", res);
    }
    
    void Clear() {
        cnt = 1; s = 0; t = n + 2;
        memset(head, 0, sizeof(head));
    }
    
    void Init() {
        n = read(); d_ = read();
        Clear();
        for(int i = 1;i <= n; i++) a[i] = read();
        sort(a + 1, a + n + 1);
        for(int i = 2;i <= n; i++) b[i] = a[i] - a[i - 1] - d_; 
        add(1, t, a[1], 0); 
        for(int i = 2;i <= n; i++) 
            if(b[i] < 0) add(s, i, -b[i], 0);
            else add(i, t, b[i], 0);
        add(n + 1, t, inf, 0);
        for(int i = 1;i <= n + 1; i++) {
            if(i != 1) add(i, i - 1, inf, 1);
            if(i != n + 1) add(i, i + 1, inf, 1);
        }
    }
    
    int main() {
    
        freopen("different.in","r",stdin); freopen("different.out","w",stdout);
    
        for(int T = read(); T ; T --) {
            Init(); Work();
        }
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    2
    4 10
    1 100 7 10
    5 1
    1 1 1 1 1
    
    1
    5 1
    1 1 1 1 1
    */
    

    接下来说说正解吧.

    正解是DP.其实我知道该用DP写这道题, 但是我不会qwq.

    首先有一个经典的转化 : (A_i = a_i - (i-1)*d). 那么问题就转化成了将(A_i)序列变成单调不降序列的最小代价.(注意(a_i)是排过序的)

    (f_{i,j})表示前(i)个元素转化成单调不降序列, 并且(A_i = j)的最小代价.那么可以列出转移方程 :

    [ f_{i,j} = |A_i - j| +min{ f_{i-1, -infty - j} } ]

    怎么优化呢? 我们可以考虑一下它的函数图像是什么, 我们如果可以找到它的最低点就好了.

    先画一下(f_{1,j} = |A_1 - j|)的图像 : 就是普通的绝对值函数图像, 没有啥好画的.

    然后在考虑一下(f_{2,j} = |A_2 - j| + min{ f_{1, -infty-j} })的图像是什么, 就是一段绝对值函数和刚刚(f_1)的前缀最小值函数合并一下, 类似一个下凸壳.

    最后(f_{n,j})的图像也是一个下凸壳, 并且斜率是连续的, 0, -1, -2, (dots), -k.

    最后的答案就是(min{ f_{n,j} }), 我们需要找到函数图像最低的点在哪里.

    (s_i)是下凸壳上定点的横坐标, 并且从大到小排序.

    那么最后的答案就是(f_{n,0} - sum_{i=1}^{n-1} i * (s_i-s_{i-1})), (i)就是那个斜率嘛, 是连续的.

    现在就考虑(f_{n_0})怎么求就好了. 因为要求(f_{n,0}), 所以(A_n = 0), 又因为(A_1 = a_1 geq 0), 所以整个(A_i)序列一定都是0, 因为要保证单调不降, 那么很自然地, (f_{n,0} = sum_{i=1}^{n} A_i).

    具体的维护这个图像的拐点要用到堆, 看下代码就好了.

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 3e5 + 5;
    long long a[N], s[N];
    priority_queue <long long> q;
    
    int main() {
    
        for(int T = read(); T ; T --) {
            int n = read(), d = read(); 
            for(int i = 1;i <= n; i++) a[i] = read();
            sort(a + 1, a + n + 1);
            for(int i = 1;i <= n; i++) {
                long long x = max(0ll, a[i] - 1ll * (i - 1) * d);
                q.push(x); q.push(x); q.pop();
            }
            int cnt = 0;
            long long res = 0;
            for(int i = 1;i <= n; i++) res += abs(a[i] - 1ll * (i - 1) * d);
            while(q.size()) s[++ cnt] = q.top(), q.pop(); s[++ cnt] = 0;
            for(int i = 1;i < cnt; i++) res -= (s[i] - s[i + 1]) * i;
            printf("%lld
    ", res);
        }
    
        return 0;
    }
    

    T3

    你有 2n+m 根绳子,每根绳子两端染有黑、白两种颜色。

    其中 n 根绳子两端都是黑色; n 根绳子两端都是白色; m 根绳子一段黑色另一端白色。

    现在绳子一端为黑色和白色的线头分别有 2n+m 个,你想要把黑色和白色一端的线头随机配对(共 (2n+m)! 种方法),每对配对的线头用胶水拼接起来。可以发现,这样所有绳子一定会绕成若干个环。

    你想要求出环的个数期望是多少,结果对 (10^9+7) 取模。

    一行两个整数 n,m。

    对于全部数据,(0leq n,mleq 10^6)

    一看期望就头疼啊md.

    直接设(f_{n,m})是两端都为0的线段有(n)条, 两端都为1的线段有(m)条, 一端为0一端为1的有(m)条的期望答案是多少. 最后输出(f_{n,m})就好了.

    然后分情况讨论, 我们拿出一条一端是0, 一端是1的来和别的组合 :

    1.和两端都是0或两端都是1的连接, 那么连完之后会得到两端都是1或两端都是0的(注意顺序), 并没有形成环, 这样的一共有(2n)种方案.

    2.和一端为1一端为0的连(不包括自己), 连完之后会得到一端为0一端为1的, 并没有形成环, 这样的一共有(m-1)种方案.

    3.自己首尾相连, 连完之后会得到一个环, 这样的一共有(1)种方案.

    那么总共有(2n+m)中方案, 有贡献的只有1个, 那么可以得到转移方程 :

    [f_{n,m} = f_{n,m-1} + frac{1}{2n+m} ]

    如果说我们知道了(f_{n,0})的话就可以(O(n))求答案了.

    两端为0的只能和两端为1的去连, 因为没有一端是0一端是1的了, 再结合上面那个式子就可以得到 :

    [f_{n,0} = f_{n-1,1} = f_{n-1,0} + frac{1}{2n-1} ]

    也是可以(O(n))求的.

    代码非常短 :

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e6 + 5, mod = 1e9 + 7;
    int n, m, ans;
    
    int ksm(int x, int y) {
        int res = 1;
        while(y) { if(y & 1) res = 1ll * res * x % mod; x = 1ll * x * x % mod; y >>= 1; }
        return res;
    }
    
    int main() {
    
        n = read(); m = read();
        for(int i = 0;i < n; i++) ans = (ans + ksm(2 * i + 1, mod - 2)) % mod;
        for(int i = 1;i <= m; i++) ans = (ans + ksm(2 * n + i, mod - 2)) % mod;
        printf("%d", ans);
    
        return 0;
    }
    
  • 相关阅读:
    LoadRunner出现error问题及解决方法总结
    使用cookie绕过登录进行性能测试
    性能测试中如何定位性能瓶颈
    关于函数lr_eval_string
    Loadrunner脚本编程(1)大体思路
    Myeclipse 9集成 TestNG
    Page Speed
    Android problems
    10204_vista_w2k8_x64_production_db oracle installed on windows7 64bits
    Don't Live with Broken windows——不能容忍破窗户
  • 原文地址:https://www.cnblogs.com/czhui666/p/14539868.html
Copyright © 2011-2022 走看看