luogu NOIp热身赛(2018-11-07)题解

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\n", 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\n", 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\n", 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\n", 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\n", ans);
}
5

T6:随机数生成器

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

首先写出递推式

$f[n] = \frac{1}{n}(\sum\limits_{i = 1}^{n} f[i]) + 1$

化简

$f[n] = \frac{n + \sum\limits_{i = 1}^{n - 1} f[i]}{n - 1}$

我们考虑用$S[n] = \sum\limits_{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] = \sum\limits_{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\n", 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 = \binom{n}{m}$

然后$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 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\n", mul(ans, C(n, k)));
    return 0;
}
7

猜你喜欢

转载自www.cnblogs.com/reverymoon/p/9927109.html