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]);
}