• LOJ6496 「雅礼集训 2018 Day1」仙人掌


    Link
    先考虑树的情况,设(f_{u,0/1})表示(u)的父亲占用的(u)的出度为(0/1)的情况下给(u)的子树内的边定向的方案数。
    转移很简单,这里就不赘述了。
    现在考虑仙人掌的情况,考虑在圆方树上dp。
    状态需要改为(f_{u,0/1/2}),圆点可以用类似于树上的形式dp,方点可以枚举环上任意一条边的方向然后递推。
    这个dp的形式类似于背包,可以用分治NTT优化。

    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<utility>
    #include<algorithm>
    using i64=long long;
    using pi=std::pair<int,int>;
    using poly=std::vector<i64>;
    const int N=100007,M=1<<18,P=998244353;
    char ibuf[1<<22],*iS=ibuf;int n,m,len,deg,rev[M],a[N],fa[N];i64 w[M],f[N][3];
    std::vector<pi>e[N];std::vector<poly>vec[N];
    int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    int getlen(int n){return 1<<(32-__builtin_clz(n));}
    void inc(i64&a,i64 b){a+=b-P,a+=a>>63&P;}
    void dec(i64&a,i64 b){a-=b,a+=a>>63&P;}
    i64 pow(i64 a,i64 b){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
    void init(int n)
    {
        int lim=1<<(len=32-__builtin_clz(n)),pos=lim/2;i64 g=pow(3,(P-1)/lim);
        for(int i=1;i<lim;++i) rev[i]=(rev[i>>1]>>1)|(i&1? pos:0);
        w[pos]=1;for(int i=pos+1;i<lim;++i) w[i]=g*w[i-1]%P;
        for(int i=pos-1;i;--i) w[i]=w[i<<1];
    }
    void NTT(i64*a,int lim,int f)
    {
        if(!~f) std::reverse(a+1,a+lim);
        for(int i=0,x=len-__builtin_ctz(lim);i<lim;++i) if(i<rev[i]>>x) std::swap(a[i],a[rev[i]>>x]);
        for(int i=1;i<lim;i<<=1) for(int j=0,d=i<<1;j<lim;j+=d) for(int k=0,x;k<i;++k) x=w[i+k]*a[i+j+k]%P,dec(a[i+j+k]=a[j+k],x),inc(a[j+k],x);
        if(!~f) for(int i=0,x=P-(P-1)/lim;i<lim;++i) a[i]=x*a[i]%P;
    }
    poly operator*(poly a,poly b)
    {
        int len=a.size()+b.size()-1,lim=getlen(len);
        a.resize(lim),NTT(&a[0],lim,1),b.resize(lim),NTT(&b[0],lim,1);
        for(int i=0;i<lim;++i) a[i]=a[i]*b[i]%P;
        return NTT(&a[0],lim,-1),a.resize(std::min(deg,len)),a;
    }
    #define mid ((l+r)/2)
    poly solve(std::vector<poly>&a,int l,int r)
    {
        if(l==r) return a[l];
        return solve(a,l,mid)*solve(a,mid+1,r);
    }
    #undef mid
    void make_ring(int u,int v)
    {
        static int cir[N];static i64 g[N][2],s[3];int cnt=s[0]=s[1]=s[2]=0;
        for(;u!=v;v=fa[v]) cir[++cnt]=v;
        for(int o=0;o<2;++o)
        {
    	g[0][0]=o,g[0][1]=!o;
    	for(int i=1,u;i<=cnt;++i) u=cir[i],g[i][0]=(g[i-1][0]*f[u][1]+g[i-1][1]*f[u][0])%P,g[i][1]=(g[i-1][0]*f[u][2]+g[i-1][1]*f[u][1])%P;
    	inc(s[2-o],g[cnt][0]),inc(s[1-o],g[cnt][1]);
        }
        vec[u].push_back({s[0],s[1],s[2]});
    }
    void dfs(int u,int from)
    {
        static int dfn[N],low[N],tim;dfn[u]=low[u]=++tim;
        for(auto[v,id]:e[u])
        {
    	if(id==from) continue;
    	if(!dfn[v])
    	{
    	    fa[v]=u,dfs(v,id),low[u]=std::min(low[u],low[v]);
    	    if(dfn[v]==low[v]) vec[u].push_back({f[v][1],f[v][0]});
    	}
    	else if(low[u]=std::min(low[u],dfn[v]),dfn[v]>dfn[u]) make_ring(u,v);
        }
        if(vec[u].empty()) {for(int i=0;i<3;++i)f[u][i]=a[u]>=i;return ;}
        deg=a[u]+1;
        poly g=solve(vec[u],0,(int)vec[u].size()-1);
        deg=std::min(deg,(int)g.size());
        for(int i=0;i<deg;++i) for(int j=0;j<3;++j) if(i+j<=a[u]) inc(f[u][j],g[i]);
    }
    int main()
    {
        fread(ibuf,1,1<<22,stdin);
        n=read(),m=read(),init(2*n);
        for(int i=1,u,v;i<=m;++i) u=read(),v=read(),e[u].emplace_back(v,i),e[v].emplace_back(u,i);
        for(int i=1;i<=n;++i) a[i]=read();
        dfs(1,0),printf("%lld",f[1][0]);
    }
    
  • 相关阅读:
    45个非常有用的Oracle查询语句(转自开源中国社区)
    Oracle创建表空间及用户
    table里面,怎么根据checkbox选择的一行中的某个单元格的值是否为空,来判断是否该选中
    点击上传按钮,文件自动上传
    如何给frame标签的src属性以及a标签的href属性自动设值
    Tomcat内存溢出的三种情况及解决办法分析
    Java中判断字符串是否为数字的五种方法
    SSH项目里面 忘记密码的邮件发送功能
    form表单提交时,action怎么带参数
    因为多余jar包,所报的错
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/13069156.html
Copyright © 2020-2023  润新知