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

    A 老洪的遍历

    题目大意 : 从一个点走到另一个点会有一定贡献,问按要求走完一些点再回到原点能拿到的最大收益

    • 给了a[i],s是a的前缀和,从i点到j点收益是 (frac{(a_i-a_j)s_is_j}{2a_ia_j}) ,拆一下就是 (frac{1}{2}(s_ifrac{s_j}{a_j}-s_jfrac{s_i}{a_i}))

    • 发现如果设第i个点的向量是 ((s_i,frac{s_i}{a_i})) , 那就是两个点的向量点乘起来除以2,就是和原点组成三角形的面积

    • 由于要回到原点,最后会形成一个封闭图形,最后让面积最大,然后就是维护凸包,计算面积即可

    Code

    Show Code
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    const int N = 1e5 + 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 Node {
        double x, y;
    }a[N], stk[N];
    
    Node operator - (const Node &a, const Node &b) {
        return (Node) {a.x - b.x, a.y - b.y};
    }
    
    double operator * (const Node &a, const Node &b) {
        return a.x * b.y - a.y * b.x;
    }
    
    bool operator < (const Node &a, const Node &b) {
        return a.x != b.x ? a.x < b.x : a.y < b.y;
    }
    
    int n, s, tp, k;
    double ans;
    
    int main() {
        freopen("visit.in", "r", stdin);
        freopen("visit.out", "w", stdout);
        n = read();
        for (int i = 1; i <= n; ++i) {
            int x = read(); s += x;
            a[i] = (Node) {1.0 * s, 1.0 * s / x};
        }
        sort(a + 1, a + n + 1);
        stk[k = ++tp] = a[1];
        for (int i = 2; i <= n; ++i) {
            while (tp > k && (stk[tp] - stk[tp-1]) * (a[i] - stk[tp-1]) >= 0) tp--;
            stk[++tp] = a[i];
        }
        k = tp;
        for (int i = n - 1; i >= 1; --i) {
            while (tp > k && (stk[tp] - stk[tp-1]) * (a[i] - stk[tp-1]) >= 0) tp--;
            stk[++tp] = a[i];
        }
        for (int i = 1; i < tp; ++i)
            ans += stk[i] * stk[i+1];
        printf("%.5lf
    ", -ans / 2);
        return 0;
    }
    

    B 老洪的神秘操作

    题目大意 : 给一个序列,每次可以区间加一个数,问让每个数在模7的意义下与0同余所需的最少操作次数

    • 看到区间加可以转换成差分,最后就是让差分数组为零,稍微想想就会发现,最后求的其实是把n个数分组,让和为0的组数尽量多

    • 0的肯定自己一组,1+6,2+5,3+4都是可以直接匹配起来的,所以最后最多剩下3个数

    • 设f[i][j][k][l]为第一个数选了i个,第二个数选了j个,第三个数选了k个,最后一组和为l,可以分出的最多组数

    • 转移的时候枚举三个数放的是哪个。空间开不下,第一维滚动压掉就行

    Code

    Show Code
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    const int N = 501;
    
    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;
    }
    
    int n, s[9], ans, f[N][N][9], g[N][N][9], a[9], cnt, mx;
    
    int main() {
        freopen("secret.in", "r", stdin);
        freopen("secret.out", "w", stdout);
        int x = 0; ans = n = read();
        for (int i = 1; i <= n; ++i) {
            int y = read();
            s[(y-x+7)%7]++; x = y;
        }
        ans -= s[0]; s[0] = 0;
        x = min(s[1], s[6]); ans -= x; s[1] -= x; s[6] -= x;
        x = min(s[2], s[5]); ans -= x; s[2] -= x; s[5] -= x;
        x = min(s[3], s[4]); ans -= x; s[3] -= x; s[4] -= x;
        for (int i = 1; i <= 6; ++i) 
            if (s[i]) a[++cnt] = i;
        memset(g, 0xcf, sizeof(g)); g[0][0][0] = 0;
        for (int i = 0; i <= s[a[1]]; ++i) {
            memcpy(f, g, sizeof(g));
            memset(g, 0xcf, sizeof(g));
            for (int j = 0; j <= s[a[2]]; ++j) {
                for (int k = 0; k <= s[a[3]]; ++k) {
                    for (int l = 0; l <= 6; ++l) {
                        int x = (l + a[1]) % 7;
                        g[j][k][x] = max(g[j][k][x], f[j][k][l] + !x);
                        x = (l + a[2]) % 7;
                        f[j+1][k][x] = max(f[j+1][k][x], f[j][k][l] + !x);
                        x = (l + a[3]) % 7;
                        f[j][k+1][x] = max(f[j][k+1][x], f[j][k][l] + !x);
                    }
                }
            }
        }
        for (int i = 0; i <= 6; ++i)
            mx = max(mx, f[s[a[2]]][s[a[3]]][i]);
        printf("%d
    ", ans - mx);
        return 0;
    }
    

    C 老洪的数组

    题目大意 : 二维数组,第一行给定,其他的是上面的加左面的,动态修改第一行的某个数,询问一个20行以内的数是什么

    • 把询问分成k组,每一组开始的时候把所有的数预处理出来,然后考虑这组询问里的修改造成的影响,时间复杂度 (O(sqrt n))

    Code

    Show Code
    #include <cmath>
    #include <cstdio>
    
    using namespace std;
    const int N = 1e5 + 25, M = 1e9 + 7;
    
    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;
    }
    
    int n, m, sz, f[21][N], fac[N], inv[N], a[N], b[N], cnt;
    
    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(int n) {
        fac[0] = 1;
        for (int i = 1; i <= n; ++i)
            fac[i] = 1ll * fac[i-1] * i % M;
        inv[n] = Pow(fac[n], M - 2);
        for (int i = n; i >= 1; --i)
            inv[i-1] = 1ll * inv[i] * i % M;
    }
    
    int C(int n, int m) {
        return 1ll * fac[n] * inv[m] % M * inv[n-m] % M;
    }
    
    int main() {
        freopen("array.in", "r", stdin);
        freopen("array.out", "w", stdout);
        n = read(); m = read();
        sz = sqrt(80 * n); Init(n + 20);
        for (int i = 1; i <= n; ++i)
            f[0][i] = read(), a[i] = -1;
        for (int t = 1; t <= m; ++t) {
            if (t % sz == 1) {
                for (int i = 1; i <= cnt; ++i)
                    f[0][b[i]] = a[b[i]], a[b[i]] = -1;
                cnt = 0;
                for (int i = 1; i <= 20; ++i)
                    for (int j = 1; j <= n; ++j)
                        if ((f[i][j] = f[i-1][j] + f[i][j-1]) >= M) f[i][j] -= M;
            }
            int od = read(), x = read(), y = read();
            if (od == 2) {
                int ans = f[x][y];
                for (int i = 1; i <= cnt; ++i)
                    if (x && y >= b[i]) ans = (ans + 1ll * C(x - 1 + y - b[i], x - 1) * (a[b[i]] - f[0][b[i]] + M)) % M;
                printf("%d
    ", ans);
            }
            else if (a[x] != -1) a[x] = y;
            else ++cnt, b[cnt] = x, a[x] = y;
        }
        return 0;
    }
    
  • 相关阅读:
    Free DIY Tour_DP
    找出直系亲属_floyd
    Constructing Roads In JGShining's Kingdom_最长上升子序列
    买卖股票_线性dp
    Common Subsequence_公共子序列
    Doing Homework_状态压缩&&位运算
    猴子选大王
    卡片游戏
    Java 2
    Java 1
  • 原文地址:https://www.cnblogs.com/shawk/p/14559294.html
Copyright © 2011-2022 走看看