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]);
}
posted @ 2020-06-08 22:48  Shiina_Mashiro  阅读(230)  评论(0编辑  收藏  举报