zoukankan      html  css  js  c++  java
  • 省选测试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;
    }
    
  • 相关阅读:
    从Oracle提供两种cube产品说开
    Sql Server DWBI的几个学习资料
    Unload Oracle data into text file
    初学Java的几个tips
    我常用的Oracle知识点汇总
    benefits by using svn
    如何在windows上使用putty来显示远端linux的桌面
    building commercial website using Microsoft tech stack
    Understand Thread and Lock
    Update google calendar by sunbird
  • 原文地址:https://www.cnblogs.com/shawk/p/14582267.html
Copyright © 2011-2022 走看看