1. 程式人生 > >[UOJ388]【UNR #3】配對樹

[UOJ388]【UNR #3】配對樹

標記 esc 翻轉 down 下標 有一個 getchar() mat 要求

uoj

description

給你一棵\(n\)個節點的樹以及一個長為\(m\)的序列,序列每個位置上的值\(\in[1,n]\),你需要求出把序列中所有長度為偶數的區間內所有數拿出來在樹上以最小代價匹配的代價之和模\(998244353\)

sol

首先拿出偶數個點在樹上匹配這個問題,根據貪心,我們一定會讓這些點在盡可能深的位置匹配。換句話說,每棵子樹中未匹配的點至多只有一個。
那麽我們考慮每一條邊,這一條邊會被計算貢獻當且僅當這條邊連接的子樹裏有奇數個選出的點。
那麽我們相當於是要求對於每一條邊,有多少個長度為偶數的區間使得這條邊連接的子樹裏有奇數個區間內的點。
考慮把這棵子樹裏的點在原序列中標記為\(1\)

,其他點標記為\(0\),然後對這個序列做一遍前綴和。
那麽我們要求的東西實際上就是,滿足\(i \equiv j \pmod 2,s_j-s_i\equiv1\pmod 2\)\((i,j)\)對數,這相當於是區間\([i+1,j]\)的和為奇數。註意這裏的\(i,j\)可以取\(0\)
然後我們就只需要對每一棵子樹維護出這個東西的數量就好啦。
暴力的話就是暴\(for\)子樹裏的每一個點,可以修改原序列的單點,也就是給前綴和的一段後綴\(+1\),用線段樹維護每個區間有多少個下標是奇數/偶數的點的前綴和是奇數,修改就相當於區間翻轉,復雜度\(O(n^2\log m)\)
然後就\(dsu\ on\ tree\)
一波,復雜度變成了\(O(n\log n\log m)\)
其實直接線段樹合並就\(O(n\log m)\)了,但是懶得寫了qaq。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e5+5;
const int mod = 998244353;
int n,m,to[N<<1],nxt[N<<1],ww[N<<1],head[N],cnt,pre[N],fst[N];
int dis[N],sz[N],son[N],rev[N<<2],ans;
struct data{
    int od,en;
    data operator + (const data &b) const{
        return (data){od+b.od,en+b.en};
    }
}t[N<<2];
void link(int u,int v,int w){
    to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;head[u]=cnt;
}
void reverse(int x,int l,int r){
    t[x].od=(r+1)/2-l/2-t[x].od;
    t[x].en=r/2-(l-1)/2-t[x].en;
    rev[x]^=1;
}
void pushdown(int x,int l,int r){
    if (!rev[x]) return;int mid=l+r>>1;
    reverse(x<<1,l,mid);reverse(x<<1|1,mid+1,r);
    rev[x]=0;
}
void modify(int x,int l,int r,int ql,int qr){
    if (l>=ql&&r<=qr) {reverse(x,l,r);return;}
    pushdown(x,l,r);int mid=l+r>>1;
    if (ql<=mid) modify(x<<1,l,mid,ql,qr);
    if (qr>mid) modify(x<<1|1,mid+1,r,ql,qr);
    t[x]=t[x<<1]+t[x<<1|1];
}
void dfs_pre(int u,int f){
    sz[u]=1;
    for (int e=head[u];e;e=nxt[e])
        if (to[e]!=f){
            dis[to[e]]=ww[e],dfs_pre(to[e],u),sz[u]+=sz[to[e]];
            if (sz[to[e]]>sz[son[u]]) son[u]=to[e];
        }
}
void add(int u){
    for (int i=fst[u];i;i=pre[i]) modify(1,1,m,i,m);
}
void cal(int u){
    int tim=(1ll*t[1].od*((m+1)/2-t[1].od)+1ll*t[1].en*(m/2+1-t[1].en))%mod;
    ans=(1ll*dis[u]*tim+ans)%mod;
}
void upt(int u,int f){
    add(u);
    for (int e=head[u];e;e=nxt[e])
        if (to[e]!=f) upt(to[e],u);
}
void dfs(int u,int f,bool keep){
    for (int e=head[u];e;e=nxt[e])
        if (to[e]!=f&&to[e]!=son[u])
            dfs(to[e],u,0);
    if (son[u]) dfs(son[u],u,1);
    for (int e=head[u];e;e=nxt[e])
        if (to[e]!=f&&to[e]!=son[u]) upt(to[e],u);
    add(u);cal(u);
    if (!keep) upt(u,f);
}
int main(){
    n=gi();m=gi();
    for (int i=1;i<n;++i){
        int u=gi(),v=gi(),w=gi();
        link(u,v,w);link(v,u,w);
    }
    for (int i=1;i<=m;++i){
        int v=gi();pre[i]=fst[v];fst[v]=i;
    }
    dfs_pre(1,0);dfs(1,0,1);printf("%d\n",ans);return 0;
}

[UOJ388]【UNR #3】配對樹