zoukankan      html  css  js  c++  java
  • luogu NOIp热身赛(2018-11-07)题解

    为什么前面的人都跑得那么快啊?

    QAQ

    T1:区间方差 

    题目大意:询问区间方差,支持单点修改

    首先把方差的式子展开,得到

    $$d = frac{a_1 + ... a_n}{n} - frac{a_1^2 + .. + a_n^2 }{n^2}$$

    那么,只需维护$sum a_i$和$sum a_i^2$即可

    (没有区间加真是良心)

    复杂度$O(n log n)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    namespace mod_zone {
        const int mod = 1e9 + 7;
        inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
        inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; }
        inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; }
        inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; }
        inline int mul(int a, int b) { return 1ll * a * b % mod; }
        inline int fp(int a, int k) {
            int ret = 1;
            for( ; k; k >>= 1, a = mul(a, a))
            if(k & 1) ret = mul(ret, a);
            return ret;
        }
    }
    using namespace mod_zone;
    
    const int sid = 500050;
    
    int n, m;
    int w[sid], s[sid], s2[sid];
    
    #define ls (o << 1)
    #define rs (o << 1 | 1)
    
    inline void upd(int o) {
        s[o] = Inc(s[ls], s[rs]);
        s2[o] = Inc(s2[ls], s2[rs]);
    }
    
    inline void build(int o, int l, int r) {
        if(l == r) { s[o] = w[l]; s2[o] = mul(w[l], w[l]); return; }
        int mid = (l+ r) >> 1;
        build(ls, l, mid);
        build(rs, mid + 1, r);
        upd(o);
    }
    
    inline void mdf(int o, int l, int r, int p, int v) {
        if(l == r) { s[o] = v; s2[o] = mul(v, v); return; }
        int mid = (l + r) >> 1;
        if(p <= mid) mdf(ls, l, mid, p, v);
        else mdf(rs, mid + 1, r, p, v);
        upd(o);
    }
    
    inline int qrys(int o, int l, int r, int ml, int mr) {
        if(ml > r || mr < l) return 0;
        if(ml <= l && mr >= r) return s[o];
        int mid = (l + r) >> 1;
        return Inc(qrys(ls, l, mid, ml, mr), qrys(rs, mid + 1, r, ml, mr));
    }
    
    inline int qryS(int o, int l, int r, int ml, int mr) {
        if(ml > r || mr < l) return 0;
        if(ml <= l && mr >= r) return s2[o];
        int mid = (l + r) >> 1;
        return Inc(qryS(ls, l, mid, ml, mr), qryS(rs, mid + 1, r, ml, mr));
    }
    
    int main() {
        n = read(); m = read();
        rep(i, 1, n) w[i] = read();
        build(1, 1, n);
        rep(i, 1, m) {
            int c = read(), a = read(), b = read();
            if(c == 1) mdf(1, 1, n, a, b);
            else {
                int S = qrys(1, 1, n, a, b);
                int S2 = qryS(1, 1, n, a, b);
                int iv = fp(b - a + 1, mod - 2);
                int ans = Dec(mul(b - a + 1, S2), mul(S, S));
                printf("%d
    ", mul(ans, mul(iv, iv)));
            }
        }
        return 0;
    }
    1

    T2:攀爬者

    题目大意:在三维平面上有$n$个点,两两高度不相同,一个人会按高度从小到大的顺序爬这些点,询问路径的总长

    两个点之间的距离为欧几里得距离

    都从小到大爬了,顺序固定了就直接模拟吧

    复杂度$O(n log n)$

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define de double
        #define le long double
        #define ri register int
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    const int sid = 50050;
    
    int n;
    struct ser {
        int x, y, z;
        friend bool operator < (ser a, ser b)
        { return a.z < b.z; }
    } t[sid];
    
    int main() {
        n = read();
        rep(i, 1, n) {
            t[i].x = read();
            t[i].y = read();
            t[i].z = read();
        }
        sort(t + 1, t + n + 1);
        de ans = 0;
        rep(i, 1, n - 1) {
            de dx = t[i + 1].x - t[i].x;
            de dy = t[i + 1].y - t[i].y;
            de dz = t[i + 1].z - t[i].z;
            ans += sqrt(dx * dx + dy * dy + dz * dz);
        }
        printf("%.3lf
    ", ans);
        return 0;
    }
    2

    T3:蜈蚣 

    题目大意:定义一段区间的权值为异或和,把一个长为$n$的序列划分成$m$段,使得各个段的权值和最大

    考虑令$f[i][j]$表示到了第$i$个点,已经划分了$j$段的最大权值

    然后枚举$f[k][j - 1]$转移即可

    复杂度$O(n^2 m)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
        tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; }
    }
    using namespace std;
    using namespace remoon;
    
    int n, m;
    int w[1005], f[1005][105];
    
    int main() {
        n = read(); m = read();
        rep(i, 1, n) w[i] = read();
        memset(f, 128, sizeof(f));
        f[0][0] = 0;
        rep(i, 1, n) {
            int val = w[i];
            drep(j, i - 1, 0) {
                rep(k, 1, m) cmax(f[i][k], f[j][k - 1] + val);
                val ^= w[j];
            }
        }
        printf("%d
    ", f[n][m]);
        return 0;
    }
    3

    T4:漂浮的鸭子

    题目大意:在一张$n$个点和$n$条边的有向图中找到权值最大的环

    从一个点开始搜索,并把沿途的点都标记,如果$dfs$到了标记过的点,那么说明是环,统计方案

    注意,标记应该具有同时性

    即被从点$i$开始的搜索标记的点和被从点$j$开始的搜索标记的点是不同的

    复杂度$O(n)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
        tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; }
        tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } 
    }
    using namespace std;
    using namespace remoon;
    
    const int sid = 500050;
    int n, cnp, tim;
    int cap[sid], vis[sid], nxt[sid], node[sid], val[sid];
    ll dis[sid], ans;
    
    inline void addedge(int u, int v, int w) {
        nxt[++ cnp] = cap[u]; cap[u] = cnp; 
        node[cnp] = v; val[cnp] = w;
    }
    
    #define cur node[i]
    inline void dfs(int o) {
        vis[o] = tim;
        for(int i = cap[o]; i; i = nxt[i])
        if(!vis[cur]) dis[cur] = dis[o] + val[i], dfs(cur);
        else if(vis[cur] == tim) cmax(ans, dis[o] - dis[cur] + val[i]);
    }
    
    int main() {
        n = read();
        rep(i, 1, n) {
            int nxt = read(), w = read();
            addedge(i, nxt, w);
        }
        rep(i, 1, n) if(!vis[i]) ++ tim, dfs(i);
        printf("%lld
    ", ans);
        return 0;
    }
    4

    T5:最大差值

    题目大意:求满足$i < j$的最大的$A_j - A_i$

    对每个$j$分别统计,那么只要维护一个前缀最小值即可

    复杂度$O(n)$

    最大差值#include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
        tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; }
        tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } 
    }
    using namespace std;
    using namespace remoon;
    
    int main() {
        int n = read();
        ll ans = -5e9, mi = 5e9;
        rep(i, 1, n) {
            ll v = read();
            cmin(mi, v); cmax(ans, v - mi);
        }
        printf("%lld
    ", ans);
    }
    5

    T6:随机数生成器

    题目大意:一个数$x$,等概率地变成$[1, x]$中的一个数,询问期望变几次变成$1$

    首先写出递推式

    $f[n] = frac{1}{n}(sumlimits_{i = 1}^{n} f[i]) + 1$

    化简

    $f[n] = frac{n + sumlimits_{i = 1}^{n - 1} f[i]}{n - 1}$

    我们考虑用$S[n] = sumlimits_{i = 1}^n f[i]$来求解$f[i]$,得到

    $(n - 1)S[n] = nS[n - 1] + n$

    化简

    $frac{S[n]}{n} = frac{S[n - 1]}{n - 1} + frac{1}{n - 1}$

    令$g[n] = frac{S[n]}{n}$

    那么得到$g[n] = sumlimits_{i = 1}^{n - 1} frac{1}{i} = H_{n - 1}$

    即$S[n] = n * H_{n - 1}$

    即$f[n] = H_{n - 2} + frac{n}{n - 1}(n geq 3)$

    注意对$n = 2$特判

    我们只需要一个能快速求调和级数的方法

    套用欧拉公式即可

    复杂度$O(1)$...

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define de double
        #define le long double
        #define ri register int
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    const double r = 0.57721566490153286060651209008240243104215933593992;
    de H(int x) {
        double res = (log(x * 1.0) + log(x + 1.0)) / 2.0;
        res += 1.0 / (6.0 * x * (x + 1));
        res -= 1.0 / (30.0 * x * x * (x + 1) * (x + 1));
        return res + r;
    }
    
    de solve(int v) {
        de ans = 0;
        if(v == 1) return 0;
        if(v == 2) return 1;
        if(v <= 2e7) {
            rep(i, 1, v - 2) ans += 1.0 / i;
            ans += (de)(v) / (de)(v - 1);
        }
        else ans += H(v - 2) + (de)(v) / (de)(v - 1);
        return ans;
    }
    
    int main() {
        int n; cin >> n;
        printf("%.5lf
    ", solve(n));
        return 0;
    }
    6

    T7:大循环

    题目大意:

    求下面这个代码的值

    int ans  = 0;
    for(a[1] = 1; a[1] <= n; a[1] ++)
        for(a[2] = 1; a[2] < a[1]; a[2] ++)
            .......
                for(a[k] = 1; a[k] < a[k - 1]; a[k] ++)
                    ans += f(q);        

    其中,$f(q)$是一个需要计算的常数

    如果循环了$S$次,那么答案为$S * f(q)$

    循环的次数等价于长为$n$的序列,序列元素在$[1, m]$中,满足严格递增的方案数

    从$m$个元素中选出$n$个出来,它们的序唯一,因此$S = inom{n}{m}$

    然后$O(n)$地计算即可

    注意$q$不要用$int$来读

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline ll read() {
            ll p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    namespace mod_zone {
        const int mod = 1e9 + 7;
        inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
        inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; }
        inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; }
        inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; }
        inline int mul(int a, int b) { return 1ll * a * b % mod; }
        inline int fp(int a, int k) {
            int ret = 1;
            for( ; k; k >>= 1, a = mul(a, a))
            if(k & 1) ret = mul(ret, a);
            return ret;
        }
    }
    using namespace mod_zone;
    
    const int sid = 500050;
    
    ll q;
    int n, m, k, ans;
    
    inline int C(int n, int m) {
        int jc1 = 1, jc2 = 1, jc3 = 1;
        rep(i, 1, n) jc1 = mul(jc1, i);
        rep(i, 1, m) jc2 = mul(jc2, i);
        rep(i, 1, n - m) jc3 = mul(jc3, i);
        jc2 = fp(jc2, mod - 2); jc3 = fp(jc3, mod - 2);
        return mul(jc1, mul(jc2, jc3));
    }
    
    int main() {
        n = read(); m = read(); 
        k = read(); q = read() % mod;
        int x = 1;
        rep(i, 0, m) {
            int v = read();
            inc(ans, mul(v, x));
            x = mul(x, q);
        }
        printf("%d
    ", mul(ans, C(n, k)));
        return 0;
    }
    7

    T8:会议座位

    题目大意:

    给定$n$个字符串

    之后,给定这$n$个字符串的新的排列,求原本下标满足$i < j$,在新排列中满足$i > j$的字符串对数

    把新的字符串排列离散化之后,问题等价于求逆序对

    复杂度$O(n log n)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    const int sid = 500050;
    
    char s[sid];
    int n, id, v[sid], t[sid];
    int son[sid][55], pos[sid];
    ll ans;
    
    inline int nxt(char c) {
        if(c >= 'a' && c <= 'z') return c - 'a';
        if(c >= 'A' && c <= 'Z') return c - 'A' + 'z' - 'a' + 1;
    }
    
    inline void insert(char *s, int p) {
        int len = strlen(s + 1);
        int rt = 1;
        rep(i, 1, len) {
            int opt = nxt(s[i]);
            if(!son[rt][opt]) son[rt][opt] = ++ id;
            rt = son[rt][opt];
        }
        pos[rt] = p;
    }
    
    inline int find(char *s) {
        int len = strlen(s + 1);
        int rt = 1;
        rep(i, 1, len) {
            int opt = nxt(s[i]);
            rt = son[rt][opt];
        }
        return pos[rt];
    }
    
    inline void upd(int v) {
        for(ri i = v; i <= n; i += i & (-i)) t[i] ++;
    }
    
    inline int qry(int v) {
        int ret = 0;
        for(ri i = v; i; i -= i & (-i))
            ret += t[i];
        return ret;
    }
    
    int main() {
        n = read();
        rep(i, 1, n) {
            scanf("%s", s + 1);
            insert(s, i);
        }
        rep(i, 1, n) {
            scanf("%s", s + 1);
            v[i] = find(s);
        }
        drep(i, n, 1) {
            ans += qry(v[i]);
            upd(v[i]);
        }
        printf("%lld
    ", ans);
        return 0;
    }
    8

    T9:生日礼物

    题目大意:求$lcm(a, b) = n$的有序$(a, b)$对数

    考虑把$n$质因子分解为$p_1^{a_1} * p_2^{a_2} ... p_k^{a_k}$

    那么对于$a$而言,它在$p_1$处取了$a_1$时,$b$有$a_1 + 1$种选择

    对$b$而言是一样的

    但是$(a_1, a_1)$这种情况算重,因此需要$- 1$

    最终答案为$prodlimits_{i = 1}^k (2 * p_i - 1)$

    复杂度$O(sqrt n)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long    
    }
    using namespace std;
    using namespace remoon;
    
    ll n, ans = 1;
    int main() {
        cin >> n;
        for(ll i = 2; 1ll * i * i <= n; i ++)
        if(n % i == 0) {
            int cnt = 0;
            while(n % i == 0) cnt ++, n /= i;
            ans *= 2 * cnt + 1;
        }
        if(n > 1) ans *= 3;
        printf("%lld
    ", ans);
        return 0;
    }
    9

    T10: HKE与他的小朋友

    题目大意:给定一个置换$f$,求$1 * f^k$

    直接置换乘法快速幂就行

    复杂度$O(n log k)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline ll read() {
            ll p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    const int sid = 300050;
    
    int n, k;
    int p[sid], f[sid], a[sid];
    
    inline void trans(int *ans, int *p1, int *p2) {
        rep(i, 1, n) f[i] = p2[p1[i]];
        rep(i, 1, n) ans[i] = f[i];
    }
    
    int main() {
        n = read(); k = read();
        rep(i, 1, n) a[i] = i;
        rep(i, 1, n) p[read()] = i;
        
        for( ; k; k >>= 1, trans(p, p, p))
        if(k & 1) trans(a, a, p);
    
        rep(i, 1, n) printf("%d ", a[i]); 
        return 0;
    }
    10

    T11:宝藏

    题目大意:

    你有$30$种物品

    支持矩形给一些物品加上一些值和查询矩阵中每种物品的奇偶性

    由于只关心奇偶性,因此把$30$种物品压成一个$int$

    那么原问题等价于矩形异或上一个数和查询矩形的异或和

    这可以用二维树状数组解决

    复杂度$O(m log^2 n)$

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    const int sid = 2555;
    
    int n, m;
    int num[sid], t[sid][sid][2][2];
    
    inline void upd(int x, int y, int v) {
        for(ri i = x; i <= n; i += (i & (-i)))
            for(ri j = y; j <= n; j += (j & (-j)))
                t[i][j][x & 1][y & 1] ^= v;
    }
    
    inline int qry(int x, int y) {
        int ret = 0;
        for(ri i = x; i; i -= (i & (-i)))
            for(ri j = y; j; j -= (j & (-j)))
                ret ^= t[i][j][x & 1][y & 1];
        return ret;
    }
    
    int main() {
        n = read(); m = read();
        rep(i, 1, m) {
            char c = gc();
            while(c != 'P' && c != 'Q') c = gc();
            if(c == 'P') {
                int xa = read(), ya = read();
                int xb = read(), yb = read(), k = read();
                rep(j, 1, 30) num[j] = 0;
                rep(j, 1, k) {
                    int a = read(), b = read();
                    num[a] += b;
                }
                int go = 0;
                rep(j, 1, 30) if(num[j] & 1) go |= (1 << j - 1);
                upd(xa, ya, go);
                upd(xb + 1, ya, go);
                upd(xa, yb + 1, go);
                upd(xb + 1, yb + 1, go);
            }
            else {
                int xa = read(), ya = read();
                int xb = read(), yb = read();
                int ans = qry(xb, yb) ^ qry(xa - 1, yb);
                ans ^= qry(xb, ya - 1) ^ qry(xa - 1, ya - 1);
                rep(j, 1, 30)
                if(ans & (1 << j - 1)) putchar('2');
                else putchar('1');
                putchar('
    ');
            }
        }
        return 0;
    }
    11

    T12:简单的函数

    题目大意:

    定义$t$为最小的不是$n$的因子的数

    定义$f(2) = 1$,并且$f(n) = f(t) + 1(n geq 3)$

    询问$f(2) * f(3) ... * f(n)$

    首先打个表,发现$f(i)$落在$[1,4]$内

    考虑对$1, 2, 3, 4$分别求解

    一. $f(n) = 1$,只有$n = 2$时成立

    二. $f(n) = 2$,由于只有$f(2) = 1$,因此$2$是最小的不是$n$的因子的数,即$n$为奇数

    三. $f(n) = 3$和$f(n) = 4$,所有能转移到偶数的$n$满足$f(n) = 4$,否则$f(n) = 3$

    我们发现,满足$f(n) = 4$的数成一些循环节

    这是因为,不妨设$n = 2^k * p$,并且$n$最小的非因子数为$v$

    如果$v / gcd(v, n)$是某个奇质数$x$,由于$v$中一定有$2$这个因子,并且$2x > x$,所以不存在$n$不包含$x$因子的情况

    类似的探讨,可以得到$v / gcd(v, n) = 2$

    那么最小的非因子数为$v$的数一定满足$n = v / 2 * p$,如果对$n$增加$2p$,那么它仍满足$v / gcd(v, n) = 2$

    也就是说$2p$是一个循环节

    那么比$2p$更大的循环节是怎么来的呢?

    一定是最小的非因子数大于$v$的循环节

    这时,$n$至少要乘个$2$,由于$n$中$2$的幂次改变了,因此不同的循环节之间不会互相影响

    易知循环节不会太多,实际上在$n leq 10^{18}$的范围中,只有$4$个

    找出循环节直接统计即可

    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll __int128
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline ll read() {
            ll p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
    }
    using namespace std;
    using namespace remoon;
    
    namespace mod_zone {
        const int mod = 1e9 + 7;
        inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
        inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; }
        inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; }
        inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; }
        inline int mul(int a, int b) { return 1ll * a * b % mod; }
        inline int fp(int a, ll k) {
            int ret = 1;
            for( ; k; k >>= 1, a = mul(a, a))
            if(k & 1) ret = mul(ret, a);
            return ret;
        }
    }
    using namespace mod_zone;
    
    ll n;
    int ans, tot;
    long long tn;
    ll cy[105];
    
    inline ll gcd(ll a, ll b) {
        return b ? gcd(b, a % b) : a;
    }
    
    inline ll nj(ll a) {
        return (a + 1) / 2;
    }
    
    inline ll solve(ll n) {
        ll now = 2;
        int pot = 3;
        while(1) {
            bool flag = 0;
            while(1) {
                if(now % pot == 0) pot ++;
                else {
                    if(pot & 1) now *= pot / gcd(pot, now), pot ++;
                    else { flag = 1; cy[++ tot] = now; }
                }
                if(flag || now > n) break;
            }
            if(now > n) break;
            now *= pot / gcd(pot, now); pot ++;
        }
    
        ll ret = 0;
        for(int i = 1; i <= tot; i ++) ret += nj(n / cy[i]);
        return ret;
    }
    
    int main() {
        cin >> tn; 
        n = tn;
        ll n2 = (n + 1) / 2 - 1;
        ll n4 = solve(n);
        ll n3 = n / 2 - 1 - n4;
        int ans = fp(2, n2);
        ans = mul(ans, mul(fp(3, n3), fp(4, n4)));
        printf("%d
    ", ans);
        return 0;
    }
    12

    T13:数列游戏

    题目大意:

    给定两个序列$A, B$

    如果$A_i$和$A_{i + 1}$不互质,那么你可以把$A_i$和$A_{i + 1}$删除,并且得到$B_i + B_{i + 1}$的得分

    之后,你需要把$A_i, A_{i + 1}, B_i, B_{i + 1}$删除,并将$A, B$按相对顺序重新编号

    当$A$中所有相邻的数互质时,游戏结束,试最大化权值

    令$f[i]$表示在$i$最后活着的情况下,$1 sim i$的最大收益

    那么转移为$f[i] = f[j] + sum(i, j)$,并且区间$[j + 1, i - 1]$可以被删除

    我们考虑区间$dp$

    令$g[i][j]$表示区间$[i, j]$可不可以被删除

    那么转移时,枚举区间$[i, j]$中两个点$a, b$

    只需要$a, b$不互质,并且区间$[i, a - 1]$,$[a + 1, b - 1]$,$[b + 1, j]$都可以删除时

    那么区间$[i, j]$就可以被删除

    这是一个$O(n^4)$的算法

    考虑优化,区间$[i, j]$中,如果可以删除完,点$i$一定可以被删除

    那么,我们枚举跟$i$配对的是$k$,只需要区间$[i + 1, k - 1]$和区间$[k + 1, j]$可以被删除即可

    这样子复杂度就是$O(n^3)$啦...

    下面这个代码优化了挺多地方,跑的还行吧...

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    namespace remoon {
        #define ri register int
        #define ll long long
        #define tpr template <typename ra>
        #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++)
        #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --)    
        #define gc getchar
        inline int read() {
            int p = 0, w = 1; char c = gc();
            while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
            while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
            return p * w;
        }
        tpr inline void cmin(ra &a, ra b) { if(a > b) a = b; }
        tpr inline void cmax(ra &a, ra b) { if(a < b) a = b; } 
        tpr inline bool ckmin(ra &a, ra b) { return (a > b) ? a = b, 1 : 0; }
        tpr inline bool ckmax(ra &a, ra b) { return (a < b) ? a = b, 1 : 0; }
    }
    using namespace std;
    using namespace remoon;
    
    #define ll long long
    
    const int sid = 805;
    
    int n;
    int a[sid], b[sid];
    bool ok[sid][sid];
    int g[sid][sid];
    ll f[sid], sb[sid];
    
    inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
    inline int len(int a, int b) { return b - a + 1; }
    
    inline bool dfs(int l, int r) {
        if(l > r) return 1;
        if(len(l, r) & 1) return 0;
        if(g[l][r] != -1) return g[l][r];
        bool flag = 0;
        for(int i = l + 1; i <= r; i += 2) {
            if(ok[l][i]) 
                flag = dfs(l + 1, i - 1) && dfs(i + 1, r);
            if(flag) break;
        }
        return g[l][r] = flag;
    }
    
    int main() {
        n = read();
        rep(i, 1, n) a[i] = read();
        rep(i, 1, n) b[i] = read();
        
        rep(i, 1, n) sb[i] = sb[i - 1] + b[i];
        rep(i, 1, n) rep(j, i, n)
        if(i != j && !(len(i + 1, j - 1) & 1) && gcd(a[i], a[j]) != 1) 
            ok[i][j] = 1;
        
        memset(g, -1, sizeof(g));
        memset(f, 128, sizeof(f));
        f[0] = 0;
        rep(i, 1, n + 1) {
            cmax(f[i], f[i - 1]);
            rep(j, 0, i - 2) 
            if(f[j] + sb[i - 1] - sb[j] > f[i] && dfs(j + 1, i - 1))
            f[i] = f[j] + sb[i - 1] - sb[j];
        }
        printf("%lld
    ", f[n + 1]);
        return 0;
    }
    13
  • 相关阅读:
    【23设计模式】总结
    【JAVA】内部类,内部接口
    【JAVA】接口
    【JAVA】抽象类,抽象方法
    【JAVA】类加载器
    【JAVA】枚举
    【JAVA】序列化
    【JAVA】异常笔记
    汇编笔记_第十一章
    汇编笔记_第十章
  • 原文地址:https://www.cnblogs.com/reverymoon/p/9927109.html
Copyright © 2011-2022 走看看