• uoj:【UNR #3】配对树


    题目描述

    题解

    考虑如果我们选出了一个偶数区间,那我们可以在树上标出这些点,考虑贪心,如果一个子树内能匹配的就尽量匹配,所以一个子树延伸上去的点不会超过一个,并且我们发现每条边最多被选一次,所以我们考虑每条边的贡献。考虑到确定一段区间后,如果一条边两端子树内的点为奇数的话,那这个区间下这条边就肯定要选。所以我们考虑一个子树内在一段偶数区间内含有奇数个点的方案数。考虑暴力,我们可以把子树内每个点在序列上对应的位置上打上标记,然后做前缀和,如果有 $l$ 和 $r(0 le l < r le m)$ 满足 $l,r$ 奇偶性相同并且 $s_l,s_r$ 奇偶性不同的话,那区间 $(l,r]$ 就是符合要求的,于是我们线段树处理即可。这样是 $O(n^2logm)$ 的,于是我们可以进行 $ ext{dsu on tree}$ ,复杂度就是 $O(nlognlogm)$ 的了。(这题一定要写读优!)

    代码

    #include <bits/stdc++.h>
    #define _(d) while(d(isdigit(c=getchar())))
    using namespace std;
    int Rd(){char c;_(!);int x=c^48;_()x=(x<<3)+(x<<1)+(c^48);return x;}
    const int N=1e5+5,P=998244353;vector<int>h[N];
    int n,m,hd[N],V[N<<1],W[N<<1],nx[N<<1],t,f[N],s[2][2][N<<2],tg[N<<2],ans,sz[N],son[N];
    void add(int u,int v,int w){
        nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
    }
    #define Ls k<<1
    #define Rs k<<1|1
    #define mid ((l+r)>>1)
    void up(int k){
        for (int i=0;i<2;i++)
            for (int j=0;j<2;j++)
                s[i][j][k]=s[i][j][Ls]+s[i][j][Rs];
    }
    void build(int k,int l,int r){
        if (l==r){s[l&1][0][k]=1;return;}
        build(Ls,l,mid);build(Rs,mid+1,r);up(k);
    }
    void Dfs(int u,int fr){
        sz[u]=1;
        for (int i=hd[u],v;i;i=nx[i]){
            if ((v=V[i])==fr) continue;
            Dfs(v,u),sz[u]+=sz[v];f[v]=W[i];
            if (sz[son[u]]<sz[v]) son[u]=v;
        }
    }
    void push(int k){
        swap(s[0][0][k],s[0][1][k]);
        swap(s[1][0][k],s[1][1][k]);
        tg[k]^=1;
    }
    void down(int k){
        push(Ls);push(Rs);tg[k]=0;
    }
    void upd(int k,int l,int r,int L,int R){
        if (L<=l && r<=R) return push(k);
        if (tg[k]) down(k);
        if (mid>=L) upd(Ls,l,mid,L,R);
        if (mid<R) upd(Rs,mid+1,r,L,R);
        up(k);
    }
    void ins(int u){
        int z=h[u].size();
        for (int i=0;i<z;i++)
            upd(1,0,m,h[u][i],m);
    }
    void upd(int u,int fr){
        ins(u);
        for (int i=hd[u];i;i=nx[i])
            if (V[i]!=fr) upd(V[i],u);
    }
    void dfs(int u,int fr,int tp){
        for (int i=hd[u];i;i=nx[i])
            if (V[i]!=fr && V[i]!=son[u])
                dfs(V[i],u,0);
        if (son[u]) dfs(son[u],u,1);
        for (int i=hd[u];i;i=nx[i])
            if (V[i]!=fr && V[i]!=son[u])
                upd(V[i],u);ins(u);
        (ans+=1ll*f[u]*(1ll*s[0][0][1]*s[0][1][1]%P+1ll*s[1][0][1]*s[1][1][1]%P)%P)%=P;
        if (!tp) upd(u,fr);
    }
    int main(){
        n=Rd(),m=Rd();
        for (int i=1,u,v,w;i<n;i++)
            u=Rd(),v=Rd(),w=Rd(),
            add(u,v,w),add(v,u,w);
        for (int i=1;i<=m;i++)
            h[Rd()].push_back(i);
        build(1,0,m);Dfs(1,0);dfs(1,0,0);
        printf("%d
    ",ans);return 0;
    }
  • 相关阅读:
    python
    UVA 10891——Game of Sum
    codeforces632E 小偷与商店
    NOIP2007——树网的核
    NOIP2014 提高组 Day2——寻找道路
    nodeoj2000——Freda's Chess
    BZOJ1012——[JSOI2008]最大数maxnumber
    poj2823-Sliding Window
    开博客了,大家好,这是ATHENS的博客。
    Linux简易APR内存池学习笔记(带源码和实例)
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12395832.html
Copyright © 2020-2023  润新知