其他分享
首页 > 其他分享> > LOJ#6289. 花朵 树链剖分+分治NTT

LOJ#6289. 花朵 树链剖分+分治NTT

作者:互联网

本来以为这道题会非常难调,但是没想到调了不到 5 分钟就 A 了.  

由于基于多项式的运算都可以方便地进行封装,所以细节就不是很多(或者说几乎没有细节)   

题意:给定一棵树,每个点有点权,求对于所有大小为 $m$ 的独立集的点权之积的和.     

数据范围:$n,m \leqslant 8 \times 10^4$.  

先考虑一个十分显然的 $O(n^2)$ 暴力:

令 $f[x][i],g[x][i]$ 分别表示点 $x$ 选/不选的情况下独立集大小为 $i$ 的点积 之和.  

考虑将 $x$ 与 $x$ 的一个儿子 $y$ 合并:$f[x][i+j]=f[x][i] \times f[y][j]$,$g$ 同理.  

然后 $x$ 的初始值是:$f[x][1]=w[x],g[x][0]=1$.    

树形DP 卡一下上界复杂度是 $O(n^2)$ 的.  

不难发现,上述 $f[x][i+j] = f[x][i] \times f[y][j]$ 是一个卷积的形式.  

如果是菊花图或者链的话可以直接用 NTT/分治NTT 来做.   

正解的话考虑进行轻重路径剖分:   

对于一条重链来说,先求出该重链中每个点轻儿子为根的多项式 $f,g$,然后对于重链中每个点都将其轻儿子与该点合并.   

最后对于一条重链进行分治,求出该重链链顶为根的多项式.   

分析一下时间复杂度: 

考虑一条重链链顶为根的子树会被卷多少次:其祖先中每一条重链都会将其贡献一次.  

那么树链剖分中一个点有 $O(\log n)$ 个祖先,而每次卷积的时候对链分治的复杂度是 $O(n \log^2 n)$.  

总复杂度就是 $O(n \log^3 n)$,但是由于树链剖分的常数比较小,跑的并不慢.   

code:  

#include <queue>
#include <cstdio>   
#include <vector>
#include <cstring> 
#include <algorithm>  
#define N 1000009 
#define ll long long 
#define mod 998244353 
#define pb push_back
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;  
int m; 
int A[N<<2],B[N<<2];      
int tim,edges,n; 
int size[N],son[N],top[N],hd[N],to[N<<1],nex[N<<1],fa[N],dep[N]; 
int dfn[N],bu[N],si[N],val[N];   
void add(int u,int v) { 
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;  
}
int ADD(int x,int y) { 
    return (ll)(x+y)%mod; 
}  
int DEC(int x,int y) { 
    return (ll)(x-y+mod)%mod; 
}  
int MUL(int x,int y) { 
    return (ll)x*y%mod; 
}
int qpow(int x,int y) { 
    int tmp=1; 
    for(;y;y>>=1,x=(ll)x*x%mod) {   
        if(y&1) tmp=(ll)tmp*x%mod; 
    }  
    return tmp; 
}
int get_inv(int x) { 
    return qpow(x,mod-2); 
}
void NTT(int *a,int len,int op) { 
    for(int i=0,k=0;i<len;++i) { 
        if(i>k) { 
            swap(a[i],a[k]); 
        }  
        for(int j=len>>1;(k^=j)<j;j>>=1); 
    }  
    for(int l=1;l<len;l<<=1) { 
        int wn=qpow(3,(mod-1)/(l<<1));  
        if(op==-1) wn=get_inv(wn);  
        for(int i=0;i<len;i+=l<<1) { 
            int w=1;  
            for(int j=0;j<l;++j) { 
                int x=a[i+j],y=(ll)w*a[i+j+l]%mod;  
                a[i+j]=(ll)(x+y)%mod;  
                a[i+j+l]=(ll)(x-y+mod)%mod;  
                w=(ll)w*wn%mod; 
            }
        }
    }
    if(op==-1) { 
        int iv=get_inv(len); 
        for(int i=0;i<len;++i) { 
            a[i]=(ll)a[i]*iv%mod;   
        }
    }
}
struct poly { 
    int len;
    vector<int>a;  
    poly() { len=0,a.clear(); } 
    void push(int x) { 
        a.pb(x),++len;
    }
    void resize(int x) {
        a.resize(x),len=x;    
    }                       
    poly operator*(const poly &b) const { 
        int lim;
        for(lim=1;lim<len+b.len-1;lim<<=1); 
        for(int i=0;i<lim;++i) A[i]=B[i]=0;
        for(int i=0;i<len;++i) A[i]=a[i];
        for(int i=0;i<b.len;++i) B[i]=b.a[i];
        NTT(A,lim,1),NTT(B,lim,1);
        for(int i=0;i<lim;++i) {    
            A[i]=(ll)A[i]*B[i]%mod;
        }
        NTT(A,lim,-1);
        poly c;
        for(int i=0;i<len+b.len-1;++i) { 
            c.push(A[i]); 
        }
        if(c.len>m+1) c.resize(m+1);
        return c;   
    }
    poly operator+(const poly &b) const {
        poly c; 
        c.resize(max(len,b.len));  
        for(int i=0;i<c.len;++i) c.a[i]=0; 
        for(int i=0;i<c.len;++i) {    
            if(i<len) c.a[i]=ADD(c.a[i],a[i]); 
            if(i<b.len) c.a[i]=ADD(c.a[i],b.a[i]);  
        }
        return c;   
    }
    poly operator-(const poly &b) const {    
        poly c;  
        c.resize(max(len,b.len));    
        for(int i=0;i<c.len;++i) c.a[i]=0;
        for(int i=0;i<c.len;++i) { 
            if(i<len) c.a[i]=ADD(c.a[i],a[i]); 
            if(i<b.len) c.a[i]=DEC(c.a[i],b.a[i]);  
        }  
        return c;  
    }
}f0[N],f1[N],g[2][N];      
struct data {
    poly f00,f01,f10,f11;           
    data operator+(const data &b) const { 
        data c;   
        c.f00=(f01*b.f00)+(f00*(b.f00+b.f10));   
        c.f11=(f11*b.f01)+(f10*(b.f11+b.f01));    
        c.f01=(f01*b.f01)+(f00*(b.f01+b.f11));      
        c.f10=(f11*b.f00)+(f10*(b.f10+b.f00));    
        return c;  
    }
}tmp;  
void dfs1(int x,int ff) {  
    fa[x]=ff,dep[x]=dep[ff]+1,size[x]=1;  
    for(int i=hd[x];i;i=nex[i]) { 
        int y=to[i];  
        if(y==ff) continue;  
        dfs1(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;
    ++si[tp];  
    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]);  
        }
    }
}
poly calc(int l,int r,int d) {     
    if(l==r) {   
        return g[d][l];  
    }
    int mid=(l+r)>>1;  
    return calc(l,mid,d)*calc(mid+1,r,d);  
}
data solve(int l,int r) {   
    if(l==r) {      
        int u=bu[l];   
        data e;   
        e.f00=f0[u];  
        e.f11=f1[u];  
        return e;  
    }
    int mid=(l+r)>>1;       
    return solve(l,mid)+solve(mid+1,r);  
}
int main() { 
    // setIO("input");  
    int x,y,z; 
    scanf("%d%d",&n,&m);    
    for(int i=1;i<=n;++i) scanf("%d",&val[i]);
    for(int i=1;i<n;++i) {
        scanf("%d%d",&x,&y); 
        add(x,y),add(y,x); 
    }
    dfs1(1,0),dfs2(1,1);       
    for(int i=1;i<=n;++i) {
        f0[i].push(1);  
        f1[i].push(0);  
        f1[i].push(val[i]);    
    }        
    for(int i=n;i>=1;--i) {
        int p=bu[i]; 
        if(top[p]==p) {
            for(int j=dfn[p];j<=dfn[p]+si[p]-1;++j) { 
                x=bu[j];         
                int p0=0,p1=0;      
                for(int e=hd[x];e;e=nex[e]) {
                    y=to[e];  
                    if(y==son[x]||y==fa[x]) continue;            
                    g[0][++p0]=f0[y]+f1[y];   
                    g[1][++p1]=f0[y];  
                }     
                if(p0) f0[x]=calc(1,p0,0);  
                if(p1) f1[x]=f1[x]*calc(1,p1,1); 
            } 
            tmp=solve(dfn[p],dfn[p]+si[p]-1);       
            f0[p]=tmp.f01+tmp.f00;  
            f1[p]=tmp.f10+tmp.f11;             
        }
    }   
    f0[1].resize(m+1); 
    f1[1].resize(m+1);  
    printf("%d\n",(ll)(f0[1].a[m]+f1[1].a[m])%mod);  
    return 0; 
}

  

标签:return,6289,剖分,LOJ,mid,poly,int,len,重链
来源: https://www.cnblogs.com/guangheli/p/13375471.html