2018冬令营模拟测试赛(十四)

2018冬令营模拟测试赛(十四)

[Problem A]prime

试题描述

T_T

Q_Q

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

考虑 \(\sum_{i=1}^n { 2^{f(i)} }\) 这个式子的组合意义,我们发现 \(2^{f(i)}\) 的意义就是 \(i\) 这个数的每个不同质因子都有选和不选两种方案,\(2^{f(i)}\) 即方案数,也就是说 \(i\) 的无平方因子的约数个数。形式化地,即

\[ 2^{f(i)} = \sum_{d|i} { \mu^2(d) } \]

我们代入到原式当中,得到

\[ \sum_{i=1}^n { 2^{f(i)} } \\\\ = \sum_{i=1}^n { \sum_{d|i} { \mu^2(d) } } \\\\ = \sum_{d=1}^n { \mu^2(d) \sum_{d|i, i \le n} { 1 } } \\\\ = \sum_{d=1}^n { \mu^2(d) \lfloor \frac{n}{d} \rfloor } \]

然而这时 \(\mu^2(d)\) 的前缀和并不好处理,我们考虑给它变个形。先理解一下 \(\mu^2(d)\) 的意义,\(\mu^2(d) = 1 \Leftrightarrow d 没有平方因子 \Leftrightarrow d 的最大完全平方约数 = 1\),假设 \(g(d)\) 就是 \(d\) 的最大完全平方约数,于是上面的条件又等价于 \(\sqrt{g(d)} = 1\),于是可以把式子变成如下形态

\[ \mu^2(d) \\\\ = [\sqrt{g(d)} = 1] \\\\ = \sum_{i|\sqrt{g(d)}} { \mu(i) } \\\\ = \sum_{i^2|g(d)} { \mu(i) } \\\\ = \sum_{i^2|d} { \mu(i) } \]

然后代入到上式中,得到

\[ \sum_{d=1}^n { \mu^2(d) \lfloor \frac{n}{d} \rfloor } \\\\ = \sum_{d=1}^n { \lfloor \frac{n}{d} \rfloor \sum_{i^2|d} { \mu(i) } } \\\\ = \sum_{i=1}^{\lfloor \sqrt{n} \rfloor} { \mu(i) \sum_{i^2|d, d \le n} { \lfloor \frac{n}{d} \rfloor } } \\\\ = \sum_{i=1}^{\lfloor \sqrt{n} \rfloor} { \mu(i) \sum_{d=1}^{\lfloor \frac{n}{i^2} \rfloor} { \lfloor \frac{\lfloor \frac{n}{i^2} \rfloor}{d} \rfloor } } \]

于是可以预处理 \(\mu(i)\) 的前缀和,然后分段算后面那坨 sigma,sigma 里面的也暴力分段算,最后时间复杂度是 \(O(\sqrt{n}\log n)\) 的(这个可以积分一下算出来)。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
#define LL long long

LL read() {
    LL x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 1000010
#define MOD 998244353
#define LL long long

bool vis[maxn];
int prime[maxn], cp, mu[maxn], su[maxn];

void init(int n) {
    mu[1] = su[1] = 1;
    rep(i, 2, n) {
        if(!vis[i]) prime[++cp] = i, mu[i] = -1;
        for(int j = 1; j <= cp && i * prime[j] <= n; j++) {
            vis[i*prime[j]] = 1;
            if(i % prime[j] == 0) {
                mu[i*prime[j]] = 0;
                break;
            }
            mu[i*prime[j]] = -mu[i];
        }
        su[i] = su[i-1] + mu[i];
    }
    return ;
}

int calc(LL n) {
    int ans = 0;
    for(LL i = 1; i <= n; ) {
        LL r = min(n / (n / i), n);
        ans += (LL)(r - i + 1) * (n / i) % MOD;
        if(ans >= MOD) ans -= MOD;
        i = r + 1;
    }
    return ans;
}

int main() {
    LL n = read();
    int m = sqrt(n + .5);
    
    init(m);
    int ans = 0;
    for(int i = 1; i <= m; ) {
        int r = min((int)sqrt(n / (n / ((LL)i * i)) + .5), m);
        ans += ((LL)(su[r] - su[i-1]) * calc(n / ((LL)i * i)) % MOD + MOD) % MOD;
        if(ans >= MOD) ans -= MOD;
        i = r + 1;
    }
    
    printf("%d\n", ans);
    
    return 0;
}

[Problem B]final

试题描述

TwT

QwQ

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

补充:\(1 \le m \le 3n\)

题解

这题和某道我之前做过的题(那场比赛的 C 题)做法一样,我们发现每个转移都是平移然后叠加上去,所以可以看成多项式乘上 \(x^t\),然后将所有状态、转移都转化成点值就可以直接做矩阵快速幂了。注意这题不需要循环卷积,直接开成 \(3n\) 大小 FFT 就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 2510
#define maxm 8200
#define MOD 998244353
#define Groot 3
#define LL long long

int A[3], all, cbit[8], trans[8][8];

bool Can(int s) {
    rep(i, 0, 1) if((s >> i & 1) && (s >> i + 1 & 1) && ((A[1] & 1) || (A[1] >> 2 & 1))) return 0;
    return 1;
}

int Pow(int a, int b) {
    int ans = 1, t = a;
    while(b) {
        if(b & 1) ans = (LL)ans * t % MOD;
        t = (LL)t * t % MOD; b >>= 1;
    }
    return ans;
}
int N, brev[maxm];
void FFT(int *a, int len, int tp) {
    int n = 1 << len;
    rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]);
    rep(i, 1, len) {
        int wn = Pow(Groot, MOD - 1 >> i);
        if(tp < 0) wn = Pow(wn, MOD - 2);
        for(int j = 0; j < n; j += 1 << i) {
            int w = 1;
            rep(k, 0, (1 << i >> 1) - 1) {
                int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD;
                a[j+k] = (la + ra) % MOD;
                a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
                w = (LL)w * wn % MOD;
            }
        }
    }
    if(tp < 0) rep(i, 0, n - 1) a[i] = (LL)a[i] * Pow(n, MOD - 2) % MOD;
    return ;
}

void prod(int *a, const int *b, const int *c) {
    rep(i, 0, N - 1) {
        a[i] += (LL)b[i] * c[i] % MOD;
        if(a[i] >= MOD) a[i] -= MOD;
    }
    return ;
}
struct Matrix {
    int A[8][8][maxm], n, m;
    Matrix() {}
    Matrix(int _, int __): n(_), m(__) {}
    Matrix operator = (const Matrix& t) {
        n = t.n; m = t.m;
        rep(i, 0, n) rep(j, 0, m) rep(k, 0, N - 1) A[i][j][k] = t.A[i][j][k];
        return *this;
    }
    Matrix operator * (const Matrix& t) const {
        Matrix ans(n, t.m);
        rep(i, 0, ans.n) rep(j, 0, ans.m) {
            int *a = ans.A[i][j];
            rep(k, 0, N - 1) a[k] = 0;
            rep(k, 0, m) prod(a, A[i][k], t.A[k][j]);
        }
        return ans;
    }
    Matrix operator *= (const Matrix& t) {
        *this = *this * t;
        return *this;
    }
} base(7, 0), tr(7, 7);
Matrix PowM(Matrix& a, int b) {
    Matrix ans = a, t = a; b--;
    while(b) {
        if(b & 1) ans *= t;
        t *= t; b >>= 1;
    }
    return ans;
}

int main() {
    int n = read(), m = read();
    rep(i, 0, 2) rep(j, 0, 2) A[i] |= (read() << j);
    
    all = 7;
    memset(trans, -1, sizeof(trans));
    rep(i, 1, all) cbit[i] = cbit[i-(i&-i)] + 1;
    rep(s, 0, all) if(Can(s)) {
        int ban = 0;
        rep(i, 0, 2) if(s >> i & 1) ban |= i < 2 ? (A[2] >> 1 - i) : (A[2] << 1);
        rep(ts, 0, all) if(Can(ts) && !(ban & ts)) {
            int tb = 0;
            rep(i, 0, 2) if(ts >> i & 1) tb |= i < 2 ? (A[0] >> 1 - i) : (A[0] << 1);
            if(tb & s) continue;
            trans[ts][s] = cbit[ts];
            // printf("%d -> %d  %d\n", s, ts, trans[ts][s]);
        }
    }
    
    N = 1; int len = 0;
    while(N <= 3 * n) N <<= 1, len++;
    rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
    
    memset(base.A, 0, sizeof(base.A));
    base.A[0][0][0] = 1;
    FFT(base.A[0][0], len, 1);
    
    memset(tr.A, 0, sizeof(tr.A));
    rep(ts, 0, all) rep(s, 0, all) {
        if(trans[ts][s] >= 0) tr.A[ts][s][trans[ts][s]] = 1;
        FFT(tr.A[ts][s], len, 1);
        // printf("tr[%d][%d]: ", ts, s); rep(i, 0, N - 1) printf("%d%c", tr.A[ts][s][i], i < N - 1 ? ' ' : '\n');
    }
    
    base = PowM(tr, n) * base;
    int ans = 0;
    rep(s, 0, all) {
        FFT(base.A[s][0], len, -1);
        // printf("%d: ", s); rep(i, 0, N - 1) printf("%d%c", base.A[s][0][i], i < N - 1 ? ' ' : '\n');
        ans += base.A[s][0][m];
        if(ans >= MOD) ans -= MOD;
    }
    printf("%d\n", ans);
    
    return 0;
}

[Problem C]gift

试题描述

TAT

QAQ

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

这题出锅啦,答案下取整改成上取整减 \(1\)

题解

看到分数就分数规划(二分),现在就是判断是否存在方案使得 \(\frac{B}{A} > x\),移项得到 \(B - Ax > 0\),于是最大化 \(B - Ax\),我们先将所有的好感度都选上,然后就有两种代价,一个是牺牲某个好感度,一个是买某个物品,于是这就是经典的最小割模型了。

用最大权闭合子图的模型会有 \(n + m + 2\) 个点,就 T 飞了,要优化。每条边可以用方程算一下定多少流量,这样就只有 \(n + 2\) 个点了,然后把重边缩掉卡卡常数就 A 了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <map>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 204010
#define maxm 416010
#define oo 2147483647

struct Edge {
    int from, to, flow;
    Edge() {}
    Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {}
};
struct Dinic {
    int s, t, n, m, head[maxn], nxt[maxm];
    Edge es[maxm];
    int vis[maxn], Q[maxn], hd, tl;
    int cur[maxn];
    
    void init() {
        m = 0; memset(head, -1, sizeof(head));
        return ;
    }
    void setn(int _) {
        n = _;
        return ;
    }
    
    void AddEdge(int a, int b, int c, int revc = 0) {
        es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++;
        es[m] = Edge(b, a, revc); nxt[m] = head[b]; head[b] = m++;
        return ;
    }
    
    bool BFS() {
        memset(vis, 0, sizeof(vis));
        vis[t] = 1;
        hd = tl = 0; Q[++tl] = t;
        while(hd < tl) {
            int u = Q[++hd];
            for(int i = head[u]; i != -1; i = nxt[i]) {
                Edge& e = es[i^1];
                if(!vis[e.from] && e.flow) vis[e.from] = vis[u] + 1, Q[++tl] = e.from;
            }
        }
        return vis[s] > 1;
    }
    
    int DFS(int u, int a) {
        if(u == t || !a) return a;
        int flow = 0, f;
        for(int& i = cur[u]; i != -1; i = nxt[i]) {
            Edge& e = es[i];
            if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) {
                flow += f; a -= f;
                e.flow -= f; es[i^1].flow += f;
                if(!a) return flow;
            }
        }
        return flow;
    }
    
    int MaxFlow(int _s, int _t) {
        s = _s; t = _t;
        int flow = 0;
        while(BFS()) {
            rep(i, 1, n) cur[i] = head[i];
            flow += DFS(s, oo);
        }
        return flow;
    }
} sol;

#define maxgift 4010
#define maxcond 200010
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)

int CntP;
struct Point {
    int id;
    Point(): id(0) {}
    int p() { return id ? id : id = ++ CntP; }
} gift[maxgift], S, T;
int n, m, sum, mn, price[maxgift], gid[maxgift], half[maxcond], val[maxcond], deg[maxgift];
map <pii, int> cond;

bool check(int x) {
    for(int i = 0; i < sol.m; i += 2) sol.es[i].flow += sol.es[i^1].flow, sol.es[i^1].flow = 0;
    rep(i, 1, n) sol.es[gid[i]].flow = price[i] * x;
    rep(i, 1, m) if(half[i] >= 0) sol.es[half[i]].flow = sol.es[half[i]^1].flow = val[i];
    int flow = sol.MaxFlow(S.p(), T.p());
    return sum - flow > 0;
}

int main() {
    n = read(); m = read(); sum = 0; mn = oo;
    sol.init();
    rep(i, 1, n) gid[i] = sol.m, sol.AddEdge(S.p(), gift[i].p(), price[i] = read() << 1), mn = min(mn, price[i]);
    memset(half, -1, sizeof(half));
    rep(i, 1, m) {
        int a = read(), b = read(); int now = read();
        if(a > b) swap(a, b);
        if(!cond.count(mp(a, b))) cond[mp(a,b)] = i;
        val[cond[mp(a,b)]] += now;
        deg[a] += now; deg[b] += now;
        sum += now << 1;
    }
    for(map <pii, int> :: iterator i = cond.begin(); i != cond.end(); i++)
        half[i->y] = sol.m, sol.AddEdge(gift[i->x.x].p(), gift[i->x.y].p(), val[i->y], val[i->y]);
    rep(i, 1, n) sol.AddEdge(gift[i].p(), T.p(), deg[i]);
    sol.setn(CntP);
    
    int l = 0, r = sum / mn + 1;
    while(r - l > 1) {
        int mid = l + r >> 1;
        if(check(mid)) l = mid; else r = mid;
    }
    printf("%d\n", l);
    
    return 0;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8278549.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值