ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

UOJ#388. 【UNR #3】配对树 树链剖分+线段树

2020-07-15 09:01:22  阅读:251  来源: 互联网

标签:UNR 剖分 int top long 树链 ADD dfn lca


这道题卡常啊 !           

出题人说 $O(n \log^2 n)$ 可过,但我写了个 $O(n \log^2 n)$ 的树剖卡了半天常数.     

最暴力的做法:枚举区间,然后跑一个树形DP 来求最小匹配.     

显然,因为要求匹配值最小,所以一定是能匹配就先匹配.   

也就是说递归完 $x$ 的所有儿子后,$x$ 的每一个儿子最多只有 1 个点还没有匹配.      

这个时间复杂度是 $O(n^3)$ 的.    

然后我们对每一条边分别考虑:   

令 $v[x]$ 表示点 $x$ 到其父亲的边权(以 1 为根),那么 $v[x]$ 能产生贡献,当且仅当一个区间中 $x$ 子树中有奇数个点.   

这个很好理解,因为如果有奇数个点,就意味着 1 个点没有被匹配到,而需要向上延伸的 $x$ 的父亲,依此类推......       

那么就枚举右端点,然后令 $f[x][0/1]$ 分别表示多少个长度为偶数的区间满足在 $x$ 的子树中有偶数/奇数个点.      

由于要求区间长度是偶数,我们可以分别以 $1,2$ 为起点各跑一次,每次同时加入两个点来保证长度为偶数.      

考虑加入 $x,y$ 后的影响:

$x$ 到 $lca$ 与 $y$ 到 $lca$ (不包括 lca 这个点)的路径上 $f[x][0]=f[x][1]$,$f[x][1]=f[x][0]+1$             

不在 $x,y$ 路径上的点 $f[x][1]$ 不变,$f[x][0] \leftarrow f[x][0]+1$.     

这个暴力修改的话是 $O(n^2)$ 的,可以获得 $50$pts.  

满分算法的话就是用树链剖分+线段树来维护上面的东西.   

我们无外乎就是要支持:每个节点维护 $f[x][0],f[x][1]$,区间加,区间交换.  

然后定义标记 $(rev,x,y)$ 表示是否要交换 $f[x][0],f[x][1]$ 的值,交换后对 $f[x][0]$,$f[x][1]$ 分别加上 $x,y$.     

时间复杂度为 $O(n \log^2 n)$,但是会有点卡常.  

这里说几个卡常技巧: 

1. 读入优化  

2. 开 long long 要比取模快.  

3. 由于上述操作中每次加的数是 1 或 -1,所以这个标记可以直接开 int,然后区间和开 long long.    

code:    

#include <cstdio>
#include <ctime>
#include <cstring> 
#include <algorithm>      
#define N 100008  
#define ll long long 
#define mod 998244353
#define lson now<<1  
#define rson now<<1|1     
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;    
int edges,n,m,tim;    
int nd[N],f[N][2],fa[N];      
int hd[N],to[N<<1],nex[N<<1],val[N<<1]; 
int dep[N],a[N],size[N],top[N],son[N],dfn[N],bu[N];    
ll ans;   
struct data {
    int rev;  
    int vx,vy;  
    ll sx,sy,sum;    
    data(int rev=0,int vx=0,int vy=0):rev(rev),vx(vx),vy(vy){}  
}s[N<<2];   
inline void add(int u,int v,int c) {         
    nex[++edges]=hd[u];   
    hd[u]=edges,to[edges]=v,val[edges]=c;  
}            
void dfs(int x,int ff) {  
    size[x]=1;  
    fa[x]=ff,dep[x]=dep[ff]+1;        
    for(int i=hd[x];i;i=nex[i]) {              
        int y=to[i];  
        if(y==ff) continue;      
        nd[y]=val[i],dfs(y,x);    
        size[x]+=size[y];  
        if(size[y]>size[son[x]]) son[x]=y;    
    }
}
void dfs2(int x,int tp) {
    top[x]=tp;  
    dfn[x]=++tim;  
    bu[tim]=x;    
    if(son[x]) dfs2(son[x],tp); 
    for(int i=hd[x];i;i=nex[i]) 
        if(to[i]!=fa[x]&&to[i]!=son[x]) 
            dfs2(to[i],to[i]);   
}
inline int get_lca(int x,int y) { 
    while(top[x]!=top[y]) {
        dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];  
    }
    return dep[x]<dep[y]?x:y;   
}      
inline void pushup(int now) {
    s[now].sx=(ll)(s[lson].sx+s[rson].sx);  
    s[now].sy=(ll)(s[lson].sy+s[rson].sy);    
}
inline void mark_rev(int now) {
    swap(s[now].sx,s[now].sy);        
    swap(s[now].vx,s[now].vy);   
    s[now].rev^=1;     
}
inline void mark_add(int now,int vx,int vy) {
    if(vx) (s[now].sx+=(ll)vx*s[now].sum);   
    if(vy) (s[now].sy+=(ll)vy*s[now].sum);   
    if(vx) (s[now].vx+=vx);  
    if(vy) (s[now].vy+=vy);    
}
inline void pushdown(int now) {
    if(s[now].rev) {
        s[now].rev=0; 
        mark_rev(lson); 
        mark_rev(rson); 
    }   
    if(s[now].vx||s[now].vy) {
        mark_add(lson,s[now].vx,s[now].vy);  
        mark_add(rson,s[now].vx,s[now].vy);  
        s[now].vx=s[now].vy=0;   
    }
}
void build(int l,int r,int now) {
    s[now]=data(); 
    s[now].sx=0; 
    s[now].sy=0;      
    if(l==r) {
        s[now].sum=nd[bu[l]];   
        return; 
    }
    int mid=(l+r)>>1;  
    build(l,mid,lson),build(mid+1,r,rson);   
    s[now].sum=(ll)(s[lson].sum+s[rson].sum)%mod;   
}
void REV(int l,int r,int now,int L,int R) {
    if(l>=L&&r<=R) {
        mark_rev(now);   
        return;  
    }
    pushdown(now); 
    int mid=(l+r)>>1;   
    if(L<=mid) REV(l,mid,lson,L,R);  
    if(R>mid)  REV(mid+1,r,rson,L,R);   
    pushup(now);   
}
void ADD(int l,int r,int now,int L,int R,int vx,int vy) {
    if(l>=L&&r<=R) {
        mark_add(now,vx,vy);  
        return; 
    }
    pushdown(now); 
    int mid=(l+r)>>1;   
    if(L<=mid)  ADD(l,mid,lson,L,R,vx,vy);  
    if(R>mid)   ADD(mid+1,r,rson,L,R,vx,vy);  
    pushup(now);  
}     
inline void upd(int x,int y) {         
    while(top[y]!=top[x]) {  
        ADD(1,n,1,dfn[top[y]],dfn[y],-1,0);  
        REV(1,n,1,dfn[top[y]],dfn[y]);     
        ADD(1,n,1,dfn[top[y]],dfn[y],0,1);         
        y=fa[top[y]];   
    }     
    if(y!=x) {
        ADD(1,n,1,dfn[x]+1,dfn[y],-1,0);  
        REV(1,n,1,dfn[x]+1,dfn[y]);   
        ADD(1,n,1,dfn[x]+1,dfn[y],0,1);  
    } 
}
void sol(int st) {
    int x,y,lca;         
    build(1,n,1);   
    for(int i=st;i<=m;i+=2) {       
        if(i+1>m) break;  
        x=a[i],y=a[i+1];        
        if(dep[x]>dep[y]) swap(x,y);        
        lca=get_lca(x,y);      
        mark_add(1,1,0);              
        upd(lca,x); 
        upd(lca,y);   
        (ans+=s[1].sy)%=mod;
    }      
}         
char *p1,*p2,buf[100000];   
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)  
int rd()
{
    int x=0; char c;   
    while(c<48) c=nc();  
    while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc();  
    return x;    
}
int main() {  
    // setIO("input");  
    n=rd(),m=rd();  
    int x,y,z;  
    for(int i=1;i<n;++i) { 
        x=rd(),y=rd(),z=rd();  
        if(z>=mod) z-=mod;  
        add(x,y,z),add(y,x,z);  
    }    
    dfs(1,0);    
    dfs2(1,1);                   
    for(int i=1;i<=m;++i) a[i]=rd();          
    sol(1),sol(2);  
    printf("%lld\n",ans);   
    return 0;   
}

  

标签:UNR,剖分,int,top,long,树链,ADD,dfn,lca
来源: https://www.cnblogs.com/guangheli/p/13303620.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有