最终得对抗自己

杂题训练(一)

前言

一开始以为是一坨数据结构, 结果是一坨杂题。

BZOJ 3306

题目大意: 给一棵带点权树, 要求支持:
1.单点点权修改。
2.子树点权最大值。
3.换根。

解题报告

据说正解是dfn序加上一些乱搞…但是lct明显是可以做的。
在lct上用multiset打标记表示子树最大值, 复杂度多个log但是好写啊。
自己zz..prepare()里面写反updata和insert, 然后没能把自己的子树最大值更新上去…RE不断*1h+

代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <climits>
#include <set>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
namespace LCT {
    const int MAXD = MAXN;
    struct Node {
        int ch[2], fa;
        bool tfa, rev;
        int val, mx;
        multiset<int> vir;
    } d[MAXD];
    inline void initialize () {
        d[0].mx = INT_MIN;
        d[0].vir.insert(INT_MIN);
    }
    inline void updata (int u) {
        d[u].mx = max(
        max(d[u].val, *d[u].vir.rbegin()),
        max(d[d[u].ch[0]].mx, d[d[u].ch[1]].mx));
    }
    void makeNode (int u, int f, int v) {
        d[u].fa = f, d[u].mx = d[u].val = v;
        d[u].vir.insert(INT_MIN);
    }
    inline void prepare (int u) {
        updata(u);
        d[d[u].fa].vir.insert(d[u].mx);
    }
    inline void rotate (int x) {
        int y = d[x].fa;
        bool f = d[y].ch[1] == x;
        d[y].ch[f] = d[x].ch[!f];
        d[d[y].ch[f]].fa = y;
        d[x].ch[!f] = y;
        d[x].fa = d[y].fa;
        d[y].fa = x;
        swap(d[x].tfa, d[y].tfa);
        if(d[x].tfa) d[d[x].fa].ch[d[d[x].fa].ch[1] == y] = x;
        updata(y), updata(x);
    }
    inline void filp (int u) {
        d[u].rev ^= 1, swap(d[u].ch[0], d[u].ch[1]);
    }
    inline void pushdown (int u) {
        if(d[u].rev) {
            filp(d[u].ch[0]), filp(d[u].ch[1]);
            d[u].rev = false;
        }
    }
    void downlazy (int u) {
        if(d[u].fa && d[u].tfa) downlazy(d[u].fa);
        pushdown(u);
    }
    inline void splay (int x) {
        downlazy(x);
        while(d[x].tfa) {
            int y = d[x].fa, z = d[y].fa;
            if(d[y].tfa) {
                if((d[z].ch[1] == y) == (d[y].ch[1] == x)) rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        updata(x);
    }
    inline void access (int u) {
        int t = u, r = 0, tmp;
        while(u) {
            splay(u);
            if(d[u].ch[1]) d[u].vir.insert(d[d[u].ch[1]].mx);
            if(r) d[u].vir.erase(d[u].vir.find(d[r].mx));
            d[d[u].ch[1]].tfa = false;
            d[d[u].ch[1] = r].tfa = true;
            updata(u);
            r = u;
            u = d[u].fa;
        }
        splay(t);
    }
    inline void makeroot (int u) {
        access(u), filp(u);
    }
    inline int maxValue (int u) {
        access(u);
        return max(*d[u].vir.rbegin(), d[u].val);
    }
    inline void modify (int u, int v) {
        access(u), d[u].val = v, updata(u);
    }
}
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+ch-'0';
    return flg ? -ret : ret;
}
char buf[20];
int main () {
    int N = getInt(), Q = getInt();
    LCT::initialize();
    for(int i = 1; i<=N; i++) {
        int f = getInt(), v = getInt();
        LCT::makeNode(i, f, -v);
    }
    for(int i = N; i>=2; i--)
        LCT::prepare(i);
    LCT::updata(1);
    while(Q --) {
        scanf("%s", buf);
        int x = getInt();
        if(buf[0] == 'V') LCT::modify(x, -getInt());
        else if(buf[0] == 'E') LCT::makeroot(x);
        else printf("%d\n", -LCT::maxValue(x));
    }
}

BZOJ 3991

题目大意: 给你一颗树, 每次把一个点的选取状态取反, 对每次操作输出操作后所有被选取点组成的子树边权和乘二。

解题报告

什么lct!虚树!
每次把dfn前驱和后继拉出来做lca算delta即可。
注意这题dfn成环, 自己脑残了总是写不过…

代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <climits>
#include <set>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+ch-'0';
    return flg ? -ret : ret;
}
struct Node {
    int v, w, nxt;
} d[MAXN*2];
int head[MAXN], etot;
inline void addEdge (int a, int b, int c) {
    etot ++;
    d[etot].v = b;
    d[etot].w = c;
    d[etot].nxt = head[a];
    head[a] = etot;
}
int dfn[MAXN], rev[MAXN], lst[MAXN], dfntot;
int sz[MAXN], top[MAXN], fa[MAXN], depu[MAXN];
LL dep[MAXN];
int dfsSize (int u, int f) {
    rev[dfn[u] = ++dfntot] = u;
    sz[u] = 1;
    depu[u] = depu[f]+1;
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f) {
            dep[d[e].v] = dep[u]+d[e].w;
            sz[u] += dfsSize(d[e].v, u);
        }
    lst[u] = dfntot;
    return sz[u];
}
void dfs (int u, int f) {
    fa[u] = f;
    if(!top[u]) top[u] = u;
    int mxp = 0;
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f && sz[d[e].v] > sz[mxp]) mxp = d[e].v;
    if(!mxp) return ;
    top[mxp] = top[u], dfs(mxp, u);
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f && d[e].v != mxp) dfs(d[e].v, u);
}
inline int getLCA (int a, int b) {
    //printf("calt:%d, %d\n", a, b);
    while(top[a] != top[b]) {
        if(depu[top[a]] < depu[top[b]]) b = fa[top[b]];
        else a = fa[top[a]];
    }
    return depu[a] < depu[b] ? a : b;
}
inline LL getDist (int a, int b) {
    int lca = getLCA(a, b);
    return dep[a]+dep[b]-dep[lca]*2LL;
}
LL ans = 0;
set<int> st;
int main () {
    int N = getInt(), M = getInt();
    for(int i = 1; i<N; i++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        addEdge(a, b, c), addEdge(b, a, c);
    }
    dfsSize(1, 0);
    dfs(1, 0);
    while(M --) {
        int u = getInt();
        set<int>::iterator it1, it2;
        int x1 = 0, x2 = 0;
        it1 = st.lower_bound(dfn[u]);
        if(st.size() == 0) {
            st.insert(dfn[u]);
            puts("0");
            continue ;
        }
        if(it1 == st.end() || *it1 != dfn[u]) { // not exist
            if(it1 != st.end()) x2 = rev[*it1];
            else x2 = rev[*st.begin()];
            if(it1 != st.begin()) {
                --it1;
                x1 = rev[*it1];
            } else it2 = st.end(), x1 = rev[*(--it2)];
            st.insert(dfn[u]);
            if(x1 != 0 && x2 != 0) ans += getDist(x1, u)+getDist(u, x2)-getDist(x1, x2);
            else if(x1+x2 != 0 && (x1 == 0 || x2 == 0)) ans += 2*getDist(x1+x2, u);
        } else {
            it2 = it1, it2++;
            st.erase(it1);
            if(it2 != st.end()) x2 = rev[*it2];
            else x2 = rev[*st.begin()];
            if(it2 != st.begin()) {
                --it2;
                x1 = rev[*it2];
            } else it2 = st.end(), x1 = rev[*(--it2)];
            if(x1 != 0 && x2 != 0) ans -= getDist(x1, u)+getDist(u, x2)-getDist(x1, x2);
            else if(x1+x2 != 0 && (x1 == 0 || x2 == 0)) ans -= 2*getDist(x1+x2, u);
        }
        printf("%lld\n", ans);
    }
}

BZOJ 2947

题目大意: 算了这题太水就不写了…
←懒人…

BZOJ 3544

题目大意: 给你一个数列, 询问数列区间和在模M意义下的最大值。

解题报告

又不是LCT
很水的…直接在set里挨个插入前缀和, 然后查询时差分, 按照贪心思想upper_bound一个相减即可。

代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <climits>
#include <iostream>
#include <set>
using namespace std;
typedef long long LL;
inline LL getInt () {
    LL ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+ch-'0';
    return flg ? -ret : ret;
}
set<LL> st;
int main () {
    int N = getInt(); LL M = getInt();
    LL sum = 0, ans = 0;
    st.insert(0);
    for(int i = 1; i<=N; i++) {
        sum = (sum+getInt()%M+M)%M;
        set<LL>::iterator it = st.upper_bound(sum);
        if(it == st.end()) it = st.begin();
        ans = max(ans, (sum-*it+M)%M);
        //printf("cur:%I64d, find:%I64d\n", sum, *it);
        st.insert(sum);
    }
    cout << ans << '\n';
}

BZOJ 2300

题目大意: 在线动态上凸包。

解题报告

就是个版…
自己计算几何太弱了..判凸包写错…叉乘写错…调了30min+

代码

#include <cstdio>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
#include <set>
using namespace std;
const int MAXN = 100010;
const int MAXQ = 200020;
struct Vector {
    int x, y;
    Vector () {}
    Vector (double a, double b):x(a), y(b) {}
    inline Vector operator + (const Vector & b) const
    {return Vector(x+b.x, y+b.y);}
    inline Vector operator - (const Vector & b) const
    {return Vector(x-b.x, y-b.y);}
};
inline int cross (const Vector & a, const Vector & b) {
    return a.x*b.y-a.y*b.x;
}
inline double getDist (const Vector & a, const Vector & b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+(ch-'0');
    return ret;
}
inline bool operator < (const Vector & a, const Vector & b) {
    if(a.x == b.x) return a.y<b.y;
    return a.x<b.x;
}
namespace DynamicHull {
    multiset<Vector> st;
    double cur_perimeter;
    void initialize (Vector a, Vector b, Vector c) { // 逆时针
        st.clear();
        st.insert(a), st.insert(b), st.insert(c);
        cur_perimeter = getDist(a, b)+getDist(b, c);
    }
    void insert (Vector vec) {
        multiset<Vector>::iterator it1 = st.lower_bound(vec), it2 = it1, tmp;
        it1--;
        if(cross(*it2-*it1, vec-*it1) <= 0) return ;
        cur_perimeter -= getDist(*it1, *it2);
        while(it1 != st.begin()) {
            tmp = it1, tmp--;
            if(cross(vec-*tmp, vec-*it1) >= 0) {
                cur_perimeter -= getDist(*tmp, *it1), st.erase(it1);
                it1 = tmp;
            } else break;
        }
        while(it2 != st.end()) {
            tmp = it2, tmp++;
            if(tmp == st.end()) break;
            if(cross(*tmp-vec, *it2-vec) <= 0) {//(3, -2)(2, 0)
                //printf("(%.2lf, %.2lf), (%.2lf, %.2lf)\n", it2->x, it2->y, tmp->x, tmp->y);
                cur_perimeter -= getDist(*tmp, *it2), st.erase(it2);
                it2 = tmp;
            } else break;
        }
        cur_perimeter += getDist(*it1, vec)+getDist(vec, *it2);
        st.insert(vec);
    }
    inline double perimeter () {return cur_perimeter;}
}
double ans[MAXQ];
Vector pos[MAXN];
int comd[MAXQ], arg[MAXQ];
bool vis[MAXN];
int main () {
    int N = getInt(), X = getInt(), Y = getInt();
    int M = getInt();
    for(int i = 1; i<=M; i++) pos[i].x = getInt(), pos[i].y = getInt();
    int Q = getInt();
    for(int i = 1; i<=Q; i++) {
        comd[i] = getInt();
        if(comd[i] == 1)
            vis[arg[i] = getInt()] = true;
    }
    DynamicHull::initialize(Vector(0, 0), Vector(X, Y), Vector(N, 0));
    for(int i = 1; i<=M; i++)
        if(!vis[i]) DynamicHull::insert(pos[i]);
    for(int i = Q; i; i--) {
        if(comd[i] == 1 && vis[arg[i]])
            vis[arg[i]] = false, DynamicHull::insert(pos[arg[i]]);
        else ans[i] = DynamicHull::perimeter();
    }
    for(int i = 1; i<=Q; i++)
        if(comd[i] == 2) printf("%.2lf\n", ans[i]);
}

BZOJ 4260

题目大意: 盗图!

解题报告

最大区间异或和模板, 注意Trie清0时一定要清根节点…浪费时间+wa*3

代码

#include <cstdio>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
#include <set>
using namespace std;
const int MAXN = 400040;
namespace Trie {
    const int MAXD =  400040*31;
    struct Node {
        int ch[2];
    } d[MAXD];
    int dtot;
    void clear() {
        dtot = 0;
        memset(d, 0, sizeof d[0]);
    }
    inline int newNode () {memset(d+(++dtot), 0, sizeof d[0]); return dtot;}
    inline void insert (int num) {
        int u = 0, bit = 29;
        while(bit>=0) {
            bool flg = (bool)(num&(1<<bit));
            if(!d[u].ch[flg])
                d[u].ch[flg] = newNode();
            u = d[u].ch[flg];
            -- bit;
        }
    }
    inline int query (int num) {
        int u = 0, bit = 29, ret = 0;
        while(bit>=0) {
            bool flg = !(bool)(num&(1<<bit));
            if(!d[u].ch[flg]) u = d[u].ch[flg = !flg];
            else u = d[u].ch[flg];
            ret |= ((int)flg)<<bit;
            -- bit;
        }
        return ret;
    }
}
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+(ch-'0');
    return flg ? -ret : ret;
}
int A[MAXN], mxv[MAXN];
int main () {
    int N = getInt();
    for(int i = 1; i<=N; i++) A[i] = getInt();
    int cur = 0;
    Trie::insert(0);
    for(int i = 1; i<=N; i++) {
        cur ^= A[i];
        Trie::insert(cur);
        mxv[i] = max(mxv[i-1], Trie::query(cur)^cur);
    }
    Trie::clear(), cur = 0;
    Trie::insert(0);
    int ans = 0;
    for(int i = N; i>=2; i--){
        cur ^= A[i];
        Trie::insert(cur);
        ans = max(ans, (Trie::query(cur)^cur)+mxv[i-1]);
    }
    printf("%d\n", ans);
}

BZOJ 1492

CDQ写炸了…但是暂时不想补…留个坑…

BZOJ 4026

题目大意: 给你一个数组, 每次询问一个下标在区间[l, r]内元素乘积的欧拉函数, 对1e6+777取模。

解题报告

一开始想错, 然后各种zz错, 和这道题玩了好久…题目还是比较不错的
把欧拉函数展开之后可以转化为二维矩阵乘积问题, 由于没有修改用主席树维护。
我的主席树为什么比别人慢那么多…

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cstdlib>
#include <vector>
using namespace std;
typedef long long LL;
const int MOD = 1000777;
const int MAXN = 50050;
vector<int> fac[MOD];
bool isnprime[MOD]; int prec[MOD/3], ptot;
int inv[MOD];
void preProcess (int lim) {
    for(int i = 2; i<lim; i++) {
        if(!isnprime[i]) {prec[++ptot] = i; fac[i].push_back(i);}
        for(int j = 1; j<=ptot&&prec[j]*i < lim; j++) {
            fac[prec[j]*i] = fac[i];
            fac[prec[j]*i].push_back(prec[j]);
            isnprime[prec[j]*i] = true;
            if(i%prec[j] == 0) break;
        }
    }
    inv[1] = 1;
    for(int i = 2; i<MOD; i++)
        inv[i] = (MOD-(LL)(MOD/i)*inv[MOD%i]%MOD)%MOD;
    fac[1].push_back(1);
}
inline int inverse (int a) {return inv[a%MOD];}
int N, Q;
namespace PST {
    const int MAXD = 6800000;
    int val[MAXD], lc[MAXD], rc[MAXD], dtot;
    int roots[MAXN], tot = 1;
    void initialize () {val[0] = 1;}
    void solidfy(int & u, int r) {
        if(!u) u = r;
        else {
            solidfy(lc[u], lc[r]);
            solidfy(rc[u], rc[r]);
        }
    }
    inline void solidfyFloor () {solidfy(roots[tot], roots[tot-1]); ++tot;}
    int pos, ins;
    void insertF (int & u, int lb, int rb, int r) {
        if(lb > pos || rb < pos) return ;
        if(!u) u = ++dtot, val[u] = val[r];
        val[u] = (LL)val[u]*ins%MOD;
        if(lb == rb) return ;
        int mid = (lb+rb)>>1;
        insertF(lc[u], lb, mid, lc[r]);
        insertF(rc[u], mid+1, rb, rc[r]);
    }
    void insert (int p, int v) {
        pos = p, ins = v;
        //printf("ins:p:%d, v:%d\n", p, v);
        insertF(roots[tot], 1, N, roots[tot-1]);
    }
    int ql, qr;
    int queryF (int & u, int lb, int rb) {
        if(!u || lb > qr || rb < ql) return 1;
        if(lb >= ql && rb <= qr) return val[u];
        int mid = (lb+rb)>>1;
        return (LL)queryF(lc[u], lb, mid)*queryF(rc[u], mid+1, rb)%MOD;
    }
    int query (int L, int R) {
        ql = L, qr = R;
        int x = queryF(roots[R], 1, N);
        return (LL)x*inverse(queryF(roots[L-1], 1, N))%MOD;
    }
}
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+(ch-'0');
    return flg ? -ret : ret;
}
int A[MAXN], lst[MOD];
int main () {
    N = getInt(), Q = getInt();
    int mxv = 0;
    PST :: initialize();
    for(int i = 1; i<=N; i++) A[i] = getInt(), mxv = max(mxv, A[i]);
    preProcess(mxv+1);
    for(int i = 1; i<=N; i++) {
        //printf("cur:%d\n", A[i]);
        for(int j = 0; j<(int)fac[A[i]].size(); j++) {
            int x = fac[A[i]][j];
            if(lst[x]) PST::insert(lst[x], (LL)inverse(x-1)*x%MOD);
            if(x != 1) PST::insert(lst[x]=i, x-1);
        }
        PST::solidfyFloor();
    }
    int lastans = 0;
    for(int i = 1; i<=Q; i++) {
        int L = getInt()^lastans, R = getInt()^lastans;
        printf("%d\n", lastans=PST::query(L, R));
    }
}
/*
10 10
1 87 267 1 3287 268 29 165 87 11
2 8
*/

月考

月考了一波, 怀疑人生。
cqbz按照排名来分考试, 分数越高, 考试座位号越靠前, 考室号称之为’段位’。
好不容易上了王者, 停课掉到钻石, 化学64pts, 这次怕不止是要掉段, 还得坠楼了…

BZOJ 3658

题目大意: 二维平面上有N个最多K种颜色的点, 问选取一根平行于x轴的线段, 这根线段覆盖的点定义为线段上方(或下方)的所有点。
称一种覆盖是合法的当这种覆盖的所有点不包含全部K种颜色最多覆盖点数。

解题报告

好像做过?重新想, 扫描线+stl乱搞。
首先把每种颜色开一个set维护扫描线状态, 然后离散化一维用树状数组维护区间和扫描线做两遍即可。
具体看代码。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cstdlib>
#include <vector>
#include <set>
#include <stack>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+(ch-'0');
    return flg ? -ret : ret;
}
struct CoordPoint {
    int x, y, c;
} pos[MAXN], tmp[MAXN];
inline bool comp (const CoordPoint & a, const CoordPoint & b) {
    if(a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}
inline bool compY (const CoordPoint & a, const CoordPoint & b) {
    if(a.y == b.y) return a.x < b.x;
    return a.y < b.y;
}
int N, K;
int C[MAXN];
inline int lowbit (int a) {return a&-a;}
inline void add (int p) {
    while(p <= N) ++C[p], p += lowbit(p);
}
inline int query (int p) {
    int ret = 0;
    while(p) ret += C[p], p -= lowbit(p);
    return ret;
}
int ans;
set<int> st[MAXN]; stack<pair<int, int> > stk;
int itot;
void solve () {
    for(int i = 1; i<=K; i++) {
        st[i].clear();
        st[i].insert(0);
        st[i].insert(itot+1);
    }
    memset(C, 0, sizeof C);
    std :: sort(tmp+1, tmp+N+1, compY);
    for(int i = 1, p; i<=N; i=p) {
        p = i;
        while(tmp[i].y == tmp[p].y && p <= N) {
            add(tmp[p].x);
            if(st[tmp[p].c].find(tmp[p].x) != st[tmp[p].c].end())
                {++p; continue ;}
            stk.push(make_pair(tmp[p].c, tmp[p].x));
            st[tmp[p].c].insert(tmp[p].x), ++p;
        }
        while(!stk.empty()) {
            int x = stk.top().second, c = stk.top().first, lb, rb;
            stk.pop();
            set<int>::iterator it = st[c].find(x);
            --it; lb = *it;
            ++it, ++it, rb = *it;
            ans = max(ans, query(rb-1)-query(lb)-1);
        }
    }
    for(int c = 1; c<=K; c++) {
        set<int>::iterator it1 = st[c].begin(), it2 = it1;
        for(++it1; it1 != st[c].end(); it2=it1, it1++)
            ans = max(ans, query(*it1-1)-query(*it2));
    }
}
bool vis[MAXN];
int main () {
    int T = getInt();
    while(T --) {
        N = getInt(), K = getInt();
        memset(vis, 0, sizeof vis);
        for(int i = 1; i<=N; i++)
            pos[i].x = getInt(), pos[i].y = getInt(), vis[pos[i].c = getInt()] = true;
        bool flg = false;
        for(int i = 1; i<=K; i++)
            if(!vis[i]) {flg = true; break;}
        if(flg) {
            printf("%d\n", N);
            continue ;
        }
        std :: sort(pos+1, pos+N+1, comp);
        itot = 1;
        memcpy(tmp, pos, sizeof pos);
        tmp[1].x = itot;
        for(int i = 2; i<=N; i++)
            if(pos[i].x != pos[i-1].x) tmp[i].x = ++itot;
            else tmp[i].x = itot;
        ans = 0;
        solve();
        for(int i = 1; i<=N; i++) tmp[i].y = -tmp[i].y;
        solve();
        printf("%d\n", ans);
    }
}

BZOJ 3772

题目大意:给你一个树和M条路径, 问任意选择两条不相同路径恰好一条路径包含另一条的概率。

解题报告

如果路径是树上全部路径怎么做呢…
把路径用dfn转化为矩形然后上主席树维护, 注意包含和dfn序之间的转换不要太想当然了, 主席树手残在cpNode的时候顺便复制了lc和rc导致不断WA样例我太弱了…
卡内存, PST开50倍MLE了, 开45倍AC。

代码

#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
inline int getInt () {
    int ret; char ch; bool flg = false;
    while((ch = getchar()) < '0' || ch > '9') flg |= (ch == '-');
    ret = ch-'0';
    while((ch = getchar()) >= '0' && ch <= '9') ret = ret*10+(ch-'0');
    return flg ? -ret : ret;
}
int N, M;
struct Node {
    int v, nxt;
} d[MAXN*2];
int head[MAXN], etot;
inline void addEdge (int a, int b) {
    etot ++;
    d[etot].v = b;
    d[etot].nxt = head[a];
    head[a] = etot;
}
namespace PST {
    const int MAXD = MAXN*45;
    int sum[MAXD], lc[MAXD], rc[MAXD];
    int dtot, roots[MAXN], tot = 1;
    int insp;
    void cNodeTo (int a, int b) {sum[b] = sum[a];}
    void insert (int & u, int lb, int rb, int r) {
        if(lb > insp || rb < insp) return ;
        if(!u) u = ++dtot, cNodeTo(r, u);
        //printf("at:%d, insp:%d, bd:(%d, %d)\n", u, insp, lb, rb);
        sum[u]++;
        if(lb == rb) return ;
        int mid = (lb+rb)>>1;
        insert(lc[u], lb, mid, lc[r]);
        insert(rc[u], mid+1, rb, rc[r]);
    }
    int ql, qr;
    int query (int u, int lb, int rb) {
        if(!u || lb > qr || rb < ql) return 0;
        if(ql <= lb && rb <= qr) return sum[u];
        int mid = (lb+rb)>>1;
        return query(lc[u], lb, mid) + query(rc[u], mid+1, rb);
    }
    void solidfy (int & u, int r) {
        if(!u) u = r;
        else {
            solidfy(lc[u], lc[r]);
            solidfy(rc[u], rc[r]);
        }
    }
    inline int & root () {return roots[tot];}
    inline int & lroot () {return roots[tot-1];}
    inline void solidfyFloor () {
        solidfy(root(), lroot());
        ++ tot;
    }
    inline int queryRect (int a, int b, int c, int d) {
        if(b >= c) swap(a, c), swap(b, d);
        ql = c, qr = d;
        int re = query(roots[b], 1, N)-query(roots[a-1], 1, N);
        return re;
    }
}
int dfn[MAXN], lst[MAXN], rev[MAXN], dfntot;
int sz[MAXN], fa[MAXN], top[MAXN], dep[MAXN];
int dfs (int u, int f) {
    fa[u] = f, dep[u] = dep[f]+1;
    sz[u] = 1;
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f) sz[u] += dfs(d[e].v, u);
    return sz[u];
}
void dfsHv (int u, int f) {
    if(!top[u]) top[u] = u;
    rev[dfn[u] = ++dfntot] = u;
    int mxp = 0;
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f && sz[d[e].v] > sz[mxp])
            mxp = d[e].v;
    if(!mxp) {lst[u] = dfntot; return ;}
    top[mxp] = top[u], dfsHv(mxp, u);
    for(int e = head[u]; e; e = d[e].nxt)
        if(d[e].v != f && d[e].v != mxp)
            dfsHv(d[e].v, u);
    lst[u] = dfntot;
}
int getLCA (int a, int b) {
    while(top[a] != top[b]) {
        if(dep[top[a]] < dep[top[b]]) b = fa[top[b]];
        else a = fa[top[a]];
    }
    return dep[a]<dep[b] ? a : b;
}
int jump (int a, int step) {
    while(step > dep[a]-dep[top[a]])
        step -= dep[a]-dep[top[a]]+1, a = fa[top[a]];
    return rev[dfn[a]-step];
}
struct Path {
    int a, b;
} prec[MAXN];
inline bool operator < (const Path & a, const Path & b) {
    return dfn[a.a] < dfn[b.a];
}
inline LL gcd (LL a, LL b) {return !b ? a : gcd(b, a%b);}
int main () {
    N = getInt(), M = getInt();
    for(int i = 1; i<N; i++) {
        int a = getInt(), b = getInt();
        addEdge(a, b), addEdge(b, a);
    }
    for(int i = 1; i<=M; i++)
        prec[i].a = getInt(), prec[i].b = getInt();
    dfs(1, 0);
    dfsHv(1, 0);
    for(int i = 1; i<=M; i++)
        if(dfn[prec[i].a] > dfn[prec[i].b])
            swap(prec[i].a, prec[i].b);
    std :: sort(prec+1, prec+M+1);
    int cur = 1;
    for(int i = 1; i<=N; i++) {
        while(cur <= M && dfn[prec[cur].a] == i) {
            PST::insp = dfn[prec[cur].b];
            PST::insert(PST::root(), 1, N, PST::lroot());
            cur++;
        }
        PST::solidfyFloor();
    }
    LL denominator = (LL)M*(M-1)/2, numerator = 0;
    for(int i = 1; i<=M; i++) {
        int a = prec[i].a, b = prec[i].b;
        int lca = getLCA(a, b);
        if(lca == a || lca == b) {
            if(lca == b) swap(a, b);
            int x = jump(b, dep[b]-dep[a]-1);
            numerator += PST::queryRect(1, dfn[x]-1, dfn[b], lst[b])-1; // 这个得函数内保证b<c
            numerator += PST::queryRect(lst[x]+1, N, dfn[b], lst[b]);
        } else numerator += PST::queryRect(dfn[a], lst[a], dfn[b], lst[b])-1;
    }
    LL g = gcd(numerator, denominator);
    numerator /= g, denominator /= g;
    printf("%lld/%lld\n", numerator, denominator);
}

Join the discussion

Your email address will not be published. Required fields are marked *