bzoj刷题记录4.15-4.16

来源:互联网 发布:软件工作室图片 编辑:程序博客网 时间:2024/06/05 04:53

bzoj刷题记录4.15-4.16


bzoj2588: Spoj 10628. Count on a tree

很漂亮的解法。
离散化之后在树上建立主席树,然后二分答案k,判断两点间小于等于k的有多少个元素,得到O(nlg2n)算法,在bzoj上会TLE。
之后发现二分答案所在区间和主席树上区间是重合的,因此可以一起二分下去,复杂度变成了O(nlgn)

#include <bits/stdc++.h>using namespace std;inline long long read() {    long long a = 0;    int c;    do c = getchar(); while(!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}const int MAXN = 101000, lgn = 18;int lc[MAXN*3*lgn], rc[MAXN*3*lgn], sum[MAXN*3*lgn], l[MAXN*3*lgn], r[MAXN*3*lgn];int root[MAXN], depth[MAXN], rk[MAXN], n, m, dx[MAXN];int top = 0;struct node {    int to, next;} edge[MAXN*2];int head[MAXN], tp = 0;void push(int i, int j){ ++tp, edge[tp] = (node){j, head[i]}, head[i] = tp; }inline int new_node(int opl, int opr){ return ++top, l[top] = opl, r[top] = opr, lc[top]=rc[top]=sum[top] = 0, top;}void build_tree(int &nd, int opl, int opr){    nd = new_node(opl, opr);    if (opl < opr)        build_tree(lc[nd], opl, (opl+opr)/2), build_tree(rc[nd], (opl+opr)/2+1, opr);}void insert(int pre, int &nd, int pos){    if (l[pre] == r[pre]) nd = new_node(pos, pos), sum[nd] = sum[pre]+1; // 记得++++++++++    else {        nd = new_node(l[pre], r[pre]);        int mid = (l[pre] + r[pre])/2;        if (pos <= mid) insert(lc[pre], lc[nd], pos), rc[nd] = rc[pre];        else insert(rc[pre], rc[nd], pos), lc[nd] = lc[pre];        sum[nd] = sum[lc[nd]] + sum[rc[nd]];    }}int query(int nd, int opl, int opr){    if (opl > opr || !nd) return 0;    if (opl == l[nd] && opr == r[nd]) return sum[nd];    int mid = (l[nd]+r[nd])/2;    if (opl > mid) return query(rc[nd], opl, opr);    else if (opr <= mid) return query(lc[nd], opl, opr);    return query(lc[nd], opl, mid)+query(rc[nd], mid+1, opr);}int fa[MAXN][lgn];void dfs(int nd, int f) // remember to insert 1 first{    fa[nd][0] = f;    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (f == to) continue;        depth[to] = depth[nd]+1;        insert(root[nd], root[to], rk[to]);        dfs(to, nd);    }}int lca(int a, int b){    if (depth[a] < depth[b]) swap(a, b);    for (int i = 0, dt = depth[a]-depth[b]; i < lgn; i++)        if ((1<<i)&dt) a = fa[a][i];    if (a == b) return a;    for (int i = lgn-1; i >= 0; i--)        if (fa[a][i] != fa[b][i])            a = fa[a][i], b = fa[b][i];    return fa[a][0];}void dx_init(){    memcpy(dx, rk, sizeof rk);    sort(dx+1, dx+n+1);}int dx_num(int num){    int l = 1, r = n, mid;    while (l <= r) {        mid = (l+r)>>1;        if (dx[mid] < num) l = mid+1;        else r = mid-1;    }    return l;}void init(){    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; i++) rk[i] = read();    dx_init();    for (int i = 1; i <= n; i++) rk[i] = dx_num(rk[i]);    for (int i = 1; i < n; i++) {        int u, v; u = read(), v = read();        push(u, v), push(v, u);    }    depth[1] = 0; build_tree(root[0], 1, n);    insert(root[0], root[1], rk[1]);    dfs(1, 0);    for (int j = 1; j < lgn; j++)        for (int i = 1; i <= n; i++)            fa[i][j] = fa[fa[i][j-1]][j-1];}int ask(int i, int j, int k){    int l = 1, r = n;    int lp = lca(i, j), g = fa[lp][0];    int a = root[i], b = root[j], c = root[lp], d = root[g];    while (l < r) {        int mid = (l+r)/2;        int tmp = sum[lc[a]]+sum[lc[b]]-sum[lc[c]]-sum[lc[d]];        if (tmp < k) k -= tmp, l = mid+1, a = rc[a], b = rc[b], c = rc[c], d = rc[d];        else r = mid, a = lc[a], b = lc[b], c = lc[c], d = lc[d];    }    return l;}int main(){    init();    int u, v, k, lastans = 0;    for (int i = 1; i <= m; i++) {        u = read(), v = read(), k = read(); u ^= lastans;        printf("%d", lastans = dx[ask(u, v, k)]);        if(i!=m) printf("\n");    }    return 0;}

bzoj1066: [SCOI2007]蜥蜴

显然最大流建图…
愉悦身心。

#include <bits/stdc++.h>using namespace std;const int MAXN = 1005, MAXM = MAXN*100, inf = 12345678;struct node {    int to, flow, next, neg;} edge[MAXM];int head[MAXN], top = 0;inline void push(int i, int j, int f){    ++top, edge[top] = (node) {j, f, head[i], top+1}, head[i] = top;    ++top, edge[top] = (node) {i, 0, head[j], top-1}, head[j] = top;}int vis[MAXN], bfstime = 0, lev[MAXN];queue<int> que;int S = 1001, T = 1002;bool bfs(){    for (que.push(S), vis[S] = ++bfstime, lev[S] = 1; !que.empty(); que.pop()) {        int f = que.front(), to, flow;        for (int i = head[f]; i; i = edge[i].next) {            if (to = edge[i].to, flow = edge[i].flow, vis[to] == bfstime || !flow)                continue;            vis[to] = bfstime, lev[to] = lev[f]+1, que.push(to);        }    }    return vis[T] == bfstime;}int dfs(int nd, int fl = inf){    if (nd == T || !fl) return fl;    int ans = 0, t;    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to, flow = edge[i].flow;        if (lev[to] != lev[nd]+1 || !flow) continue;        t = dfs(to, min(fl, flow));        ans += t, edge[i].flow -= t;        fl -= t, edge[edge[i].neg].flow += t;    }    if (fl) lev[nd] = -1;    return ans;}int dinic(){    int ans = 0;    while (bfs()) ans += dfs(S);    return ans;}int r, c, d;inline int number(int i, int j, int tp){    return (i-1)*c+j+tp*r*c;}char str[40];int main(){    scanf("%d%d%d", &r, &c, &d);    for (int i = 1; i <= r; i++) {        scanf("%s", str+1);        for (int j = 1; j <= c; j++) {            push(number(i, j, 0), number(i, j, 1), str[j]-'0');            if (i <= d || j <= d || r-i+1 <= d || c-j+1 <= d)                push(number(i, j, 1), T, inf);        }    }    for (int i = 1; i <= r; i++)        for (int j = 1; j <= c; j++)            for (int k = 1; k <= r; k++)                for (int l = 1; l <= c; l++)                    if (!(i==k && j==l) && abs(i-k)+abs(j-l) <= d)                        push(number(i, j, 1), number(k, l, 0), inf);    int cnt = 0;    for (int i = 1; i <= r; i++) {        scanf("%s", str+1);        for (int j = 1; j <= c; j++)            if (str[j] == 'L')                push(S, number(i, j, 0), 1), cnt++;    }    cout << cnt-dinic() << endl;    return 0;}

bzoj3673/3674: 可持久化并查集

学习了一发rope大法。一个trick是在路径压缩时如果没有修改就不要replace了,否则会mle.

3673

#include <bits/stdc++.h>#include <ext/rope>using namespace std;using namespace __gnu_cxx;const int MAXN = 2e4+5;rope<int> *fa[MAXN];int findfa(int i, int nd){    if (fa[i]->at(nd)) {        int p = findfa(i, fa[i]->at(nd));        fa[i]->replace(nd, p);        return p;    }    return nd;}void link(int T, int i, int j){    int a = findfa(T, i), b = findfa(T, j);    if (a != b) fa[T]->replace(a, b);}bool linked(int T, int i, int j){ return findfa(T, i) == findfa(T, j); }int n, m;int a[MAXN];int main(){    scanf("%d%d", &n, &m);    memset(a, 0, sizeof a);    fa[0] = new rope<int>(a, a+n+1);    for (int i = 1; i <= m; i++) {        int tp; scanf("%d", &tp);        fa[i] = new rope<int>(*fa[i-1]);        if (tp == 1) {            int u, v; scanf("%d%d", &u, &v);             link(i, u, v);        } else if (tp == 2) {            int k; scanf("%d", &k);             if (k >= i) continue;            fa[i] = new rope<int>(*fa[k]);        } else {            int u, v; scanf("%d%d", &u, &v);            printf("%d\n", linked(i, u, v));        }    }    return 0;}

3674

#include <bits/stdc++.h>#include <ext/rope>using namespace std;using namespace __gnu_cxx;const int MAXN = 2e5+5;rope<int> *fa[MAXN];int findfa(int i, int nd){    if (fa[i]->at(nd)) {        int p = findfa(i, fa[i]->at(nd));        if (fa[i]->at(nd) != p) fa[i]->replace(nd, p);        return p;    }    return nd;}void link(int T, int i, int j){    int a = findfa(T, i), b = findfa(T, j);    if (a != b) fa[T]->replace(a, b);}bool linked(int T, int i, int j){ return findfa(T, i) == findfa(T, j); }int n, m;int a[MAXN];int main(){    scanf("%d%d", &n, &m);    memset(a, 0, sizeof a);    fa[0] = new rope<int>(a, a+n+1);    int lastans = 0;    for (int i = 1; i <= m; i++) {        int tp; scanf("%d", &tp);        fa[i] = new rope<int>(*fa[i-1]);        if (tp == 1) {            int u, v; scanf("%d%d", &u, &v); u ^= lastans, v ^= lastans;            link(i, u, v);        } else if (tp == 2) {            int k; scanf("%d", &k); k ^= lastans;            if (k >= i) continue;            fa[i] = new rope<int>(*fa[k]);        } else {            int u, v; scanf("%d%d", &u, &v); u ^= lastans, v ^= lastans;            printf("%d\n", linked(i, u, v)); lastans = linked(i, u, v);        }    }    return 0;}

接下来是一波莫比乌斯反演练习

莫比乌斯函数定义为:

μ(d)=[p,p(i)1](1)p(i)

符号p(k)为k中蕴含素因子p的个数。

bzoj2440: [中山市选2011]完全平方数

包含平方数其素数分解中某一项幂次大于等于2。容易想到和μ的关系。

根据容斥原理可以列出小于x的符合条件的数总数为:

i[p,p(i)1](1)p(i)xi2

前两个东西的乘积就是μ的定义,因此可以整理为:

iμ(i)xi2

由于μ是积性函数,即对于gcd(i,j)=1,μ(ij)=μ(i)μ(j),可以用线性筛搞出来。之后只要二分答案就好了。

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;int not_prime[MAXN], prime[MAXN], top = 0, mu[MAXN];void get_prime(int n){    memset(not_prime, 0, sizeof not_prime);    mu[1] = 1;    for (int i = 2; i <= n; i++) {        if (!not_prime[i]) prime[++top] = i, mu[i] = -1;        for (int j = 1; j <= top && prime[j]*i <= n; j++) {            not_prime[prime[j]*i] = 1;            if (i%prime[j] == 0) { mu[i*prime[j]] = 0; break; }            mu[i*prime[j]] = -mu[i];        }    }}long long ans(long long n){    long long tp = (long long)(sqrt(n)+0.05);    long long ans = 0;    for (long long i = 1; i <= tp; i++)        ans += mu[i]*(n/(i*i));    return ans;}int main(){    get_prime(100000);    int T; scanf("%d", &T);    while (T--) {        long long n; scanf("%lld", &n);        long long l = 1, r = 1e10;        while (l <= r) {            long long mid = (l+r)/2;            if (ans(mid) < n) l = mid+1;            else r = mid-1;        }        cout << l << endl;    }    return 0;}

bzoj2301: [HAOI2011]Problem b

基本上一下午就在肝这个题…感觉理解力捉急。

首先学习一个μ的基本性质:

d|nμ(d)=[n=1]

以及莫比乌斯反演定理:

f(n)=d|nF(d)F(n)=d|nμ(nd)f(d)

和另一种表现形式:

F(i)=k1f(ki)f(i)=k1μ(k)F(ki)

后一种更常用一些,因为处理k=1,2,3是常见的。这两个定理的证明都依赖于上面的性质。

考虑所求的可以用如下式配合容斥原理完成:

f(i)=1xp1yq[(x,y)=i]

设:

F(i)=x,y[i|(x,y)]

则有:

F(i)=x,y[i|x][i|y]=xp[i|x]yq[i|y]=xp[i|x]itq[i|it]=x[i|x]qi=piqi

由于:

F(i)=k1x,y[ki=(x,y)]=k1f(ki)

根据反演定理:

f(i)=k1μ(k)F(ik)=k1μ(k)pikqik

由于后面的除法后取整最多只有O(n)种取值,可以将许多项连续处理。考虑等式的成立条件:

pik=pik

由于:

pik=pik+c1pik=pik+c2

满足0c<1,联立以上若干式,可得:

0pikpik<1

满足:

kpi/pik(1)

由于对称性可以知道:

kqi/qik(2)

由此可以知道,于k处取到的值知道(1)(2)同时满足,两个底的乘积不变,记最大的klast。于是用分配律提出之,和式内的μ可以通过前缀和预处理之,则可以:

    ans += (sum[last]-sum[i-1])*(p/i/k)*(q/i/k);    k = last+1;

就得到了O(nn)处理f(p,q,i)的算法,再由容斥原理简单处理即可。

#include <bits/stdc++.h>using namespace std;const int MAXN = 50005;int prime[MAXN], not_prime[MAXN], mu[MAXN], s[MAXN], top = 0, n;void get_prime(){    mu[1] = 1;    for (int i = 2; i <= n; i++) {        if (!not_prime[i]) { prime[++top] = i, mu[i] = -1; }        for (int j = 1; j <= top && prime[j]*i <= n; j++) {            not_prime[prime[j]*i] = 1;            if (i%prime[j] == 0) {                mu[prime[j]*i] = 0;                break;            }            mu[prime[j]*i] = -mu[i];        }    }}void init(){    n = 50000;    get_prime();    s[0] = 0;    for (int i = 1; i <= n; i++) s[i] = s[i-1] + mu[i];}int f(int x, int p, int q){    int ans = 0, last = 0;    if (p > q) swap(p, q);    p /= x, q /= x;    for (int i = 1; i <= p; i = last+1) {        last = min(p/(p/i), q/(q/i));        ans += (s[last]-s[i-1])*(p/i)*(q/i);    }    return ans;}int f2(int i, int p, int q){    int ans = 0;    for (int k = 1; (p/(k*i))*(q/(k*i)); k++)        ans += mu[k]*(p/(k*i))*(q/(k*i));    return ans;}int main(){    int a, b, c, d, k;    init();    int n; scanf("%d", &n);    for (int i = 1; i <= n; i++) {        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);        int ans;        printf("%d\n", ans = f(k, b, d)-f(k, a-1, d)-f(k, b, c-1)+f(k, a-1, c-1));    }    return 0;}

ps:如果能不构造F直接用处理和式解决问题岂不是很棒棒吗?

0 0
原创粉丝点击