• 省选测试48


    A 随机除法

    题目大意 : 一个1e24的数,每次在因数里等概率选一个除去,问期望多少次变成1

    • 设f[i]表示i到1的期望次数,f[1]=0, 则有

    [f[n]=frac{sum_{d|n}f[d]}{sum _{d|n}1}+1 ]

    [f[n]=frac{1+sum_{d|n,d<n}f[d]}{-1+sum _{d|n}1} ]

    • 而且发现一个数的期望次数之与质因数分解后每项的幂次集合有关

    • 集合最大是18,状态数不超过200000,于是搜出所有状态,类似高维前缀和的预处理dp数组就好了

    Code

    Show Code
    #include <map>
    #include <cstdio>
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const ll MN = 1e12;
    const int N = 2e5 + 5, 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 p[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};
    int dc[N], c[N][20], cnt, s[N][20], f[N], m;
    ull pw[90], h[N];
    map<ull, int> mp;
    char ch[30];
    
    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 Dfs(long double n, int m, int x, int lt) {
        for (int i = 1; i <= 18; ++i) h[lt] += pw[c[lt][i]];
        mp[h[lt]] = lt;
        for (int i = 1; i <= m && n * p[x] <= 1e24; ++i) {
            c[++cnt][x] = i; dc[cnt] = dc[lt] * (i + 1);
            for (int j = 1; j < x; ++j) c[cnt][j] = c[lt][j];
            Dfs(n *= p[x], i, x + 1, cnt);
        }
    }
    
    int main() {
        freopen("div.in", "r", stdin);
        freopen("div.out", "w", stdout);
        for (int i = pw[0] = 1; i <= 80; ++i)
            pw[i] = pw[i-1] * 13331;
        dc[1] = cnt = 1; Dfs(1, 80, 1, 1);
        for (int i = 2; i <= cnt; ++i) {
            for (int j = 18; j >= 1; --j)
                if (c[i][j] && (s[i][j] = s[i][j+1] + s[mp[h[i]-pw[c[i][j]]+pw[c[i][j]-1]]][j]) >= M) s[i][j] -= M;
            f[i] = 1ll * (s[i][1] + dc[i]) * Pow(dc[i]-1, M-2) % M;
            for (int j = 1; j <= 18; ++j)
                if ((s[i][j] += f[i]) >= M) s[i][j] -= M;
        }
        while (scanf("%s%d", ch, &m) == 2) {
            ll a = 0, b = 0; ull ha = 0;
            for (int i = 0; ch[i]; ++i)
                b = b * 10 + ch[i] - '0', a = a * 10 + b / MN, b %= MN;
            for (int i = 1; i <= m; ++i) {
                int x, tot = 0; scanf("%d", &x);
                while ((a % x * MN + b) % x == 0)
                    b += a % x * MN, a /= x, b /= x, tot++;
                ha += pw[tot];
            }
            for (int i = m + 1; i <= 18; ++i) ha += pw[0];
            printf("%d
    ", f[mp[ha]]);
        }
    }
    

    B 炮塔

    题目大意 : 给一行格子,干扰器可以捡或放,炮台两边至少有一个干扰器才能通过,问在某一时刻最多能有多少个干扰器

    • 记录x表示当前手里有多少个干扰器,s1表示如果手里至少有一个干扰器,能回到前面拿多少个干扰器,s2表示如果手里至少有两个干扰器,能回到前面拿多少个干扰器

    • 然后就开始分类讨论

      • 如果i是空地,直接走

      • 如果i有干扰器,拿走

      • 如果i是炮台

        • 如果i+1是空地,需要在i-1放一个干扰器,后面拿到一个干扰器就能拿到这个干扰器

        • 如果i+1是干扰器,直接走过去,但之前的s1都需要在后面拿到两个干扰器才能拿到这些干扰器

        • 如果i+1是炮台,i+2是干扰器,那在i-1放下这个干扰器,别的都不变,若i+2不是干扰器,就走不动了

    Code

    Show Code
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    const int N = 4e5 + 5;
    
    char c[N];
    
    int main() {
        freopen("tower.in", "r", stdin);
        freopen("tower.out", "w", stdout);
        int T; scanf("%d", &T);
        while (T--) {
            scanf("%s", c);
            int n = strlen(c); c[n] = c[n+1] = c[n+2] = '#';
            int x = 0, s1 = 0, s2 = 0, ans = 0;
            for (int i = 0; c[i]; ++i) {
                if (c[i] == '.');
                else if (c[i] == '*') x++;
                else if (c[i+1] == '.') { if (x) x--, s1++; else break; }
                else if (c[i+1] == '*') s2 += s1, s1 = 0;
                else if (c[i+2] == '*') { if (x) x--,  i++; else break; }
                else break;
                if (x >= 1) x += s1, s1 = 0;
                if (x >= 2) x += s2, s2 = 0;
                if (x > ans) ans = x;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    C 最大子段和

    题目大意 : 找最大子段和,小b写的是找最大的正数区间,给出一个序列的奇数位置的值,自己填偶数位置,问小b的误差最大是多少

    • 首先一个结论是要么填-1要么填k,填负数是为了把正数区间分开,所以要填一个最大的,填正数的话大了也不会是答案更差,但有可能会更优,所以也填最大的

    • 于是就可以枚举最大子段区间[l,r],设f[i][j]表示前i个位置,偶数位填了j个-1,小b计算值的最小值,s[i]表示偶数位置全填k的前缀和

    • 就有f[i][j]=min{max(f[p-1][j-!(p&1)],s[i]-s[p])},要满足a[p]<0,a[p+1~i]都为正数,如果p是偶数位置可以填一个-1,如果p是奇数位就要强制转移了

    • 这就是n4的dp

    • 然后发现在最大子段和区间两边放k,让区间扩为[1,n]是不会更劣的,如果1,n位置是负数就不能扩到最边上了

    • 于是就不用枚举区间,可以n3了

    • 然后会发现f[i][j]在j相同时,转移点p是单调的,而且对于p为偶数的情况,f[p-1][j-1]是单调不降的,s[i]-s[p]是单调不升的

    • 于是在两个函数交叉点两边会有可能是最优决策点,而发现i变大后,s[i]-s[p]一定会变大,不会变小,所有决策点p一定是单调的

    • 于是就可以n2了

    Code

    Show Code
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    const int N = 1e4 + 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;
    }
    
    int n, k, a[N], m, f[N][N>>1], ans, s[N], sum;
    
    int main() {
        freopen("subsegment.in", "r", stdin);
        freopen("subsegment.out", "w", stdout);
        n = read(); k = read(); m = n * 2 - 1;
        for (int i = 1; i <= m; i += 2) 
            a[i] = read(), sum += a[i];
        if (a[1] < 0) sum -= a[1], a[1] = 0;
        if (a[m] < 0) sum -= a[m], a[m] = 0;
        for (int i = 1; i <= m; ++i)
            s[i] = s[i-1] + (i & 1 ? a[i] : k);
        memset(f, 0x3f, sizeof(f)); f[0][0] = 0;
        for (int j = 0; j < n; ++j) {
            int l = 0, p = 0;
            for (int i = 1; i <= m; ++i) {
                if (a[i] < 0) l = i, p = i - 1;
                if (l) f[i][j] = max(f[l-1][j], s[i] - s[l]);
                else f[i][j] = s[i];
                for (; p + 2 <= i && s[i] - s[p+2] >= f[p+1][j-1]; p += 2);
                if (l < p) f[i][j] = min(f[i][j], s[i] - s[p]);
                f[i][j] = min(f[i][j], f[p+1][j-1]);
            }
            ans = max(ans, sum + (n - j - 1) * k - j - f[m][j]);
        }
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    hdu 4460spfa用map来实现
    hdu 2579
    hdu 2845
    hdu 4462
    hdu 4557
    hdu 4639
    URAL 2078 Bowling game
    UVA
    HDU 5773 The All-purpose Zero 脑洞LIS
    Codeforces Round #368 (Div. 2) C. Pythagorean Triples 数学
  • 原文地址:https://www.cnblogs.com/shawk/p/14582267.html
Copyright © 2020-2023  润新知