题面:https://loj.ac/problem/6496
题解
好毒瘤的一道题
先把仙人掌转成圆方树
我们考虑DP
设f[u][0/1/2]表示点u如果向上方连0/1/2条出边,以u为根的子树的所有边的定向的方案数
如果一个点u是圆点
那么点u的答案f[u][0/1/2]就是满足(u的所有儿子贡献的出度和<=a[u]-(0/1/2))的所有方案数
这种转移可以写成卷积的形式,而且每个多项式的最高次幂不超过2,可以进行分治NTT优化
(这里的方点(原图的环)就可以看作可以贡献两个出度的儿子)
如果一个点u是方点
那么方点的父亲就是该环的顶部
我们枚举顶部左端的第一条边的方向(指向顶部或不指向顶部)
那么一个环上的方案就可以递推出来了
红色的边为已经定向的边,绿色的边表示我们正在枚举计算的边
设g0,g1表示当前点v被前一个点(指向/不指向)的方案数
t0,t1表示下一个点被v(指向/不指向)的方案数
那么显然有
t0=g0*f[v][1]+g1*f[v][2]
t1=g0*f[v][0]+g1*f[v][1]
(因为一个点v最多只有一个方父亲,相当于v可以向u连两个出边)
但是当前的方点u的父亲会由于我们的定向不同而向下连不同条出边
所以我们一开始要单独枚举定向,分别算出不同定向方案对f[u][0/1/2]的贡献
(其实这里的方点的f[u][0/1/2]就表示环u可以向环顶连0/1/2条出边的方案数)
代码:(这题会有重边)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 400005
int fir[N],to[2*N],nxt[2*N],d[N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
int dfn[N],low[N],stk[N],top,dc;
vector<int> G[N],id[N];bool vis[N];
int val[N],tot;
void dfs(int u)
{
dfn[u]=low[u]=++dc;
stk[++top]=u;
for(int v,i=0;i<(int)G[u].size();i++){
if(vis[id[u][i]])continue;
vis[id[u][i]]=1;
if(!dfn[v=G[u][i]]){
dfs(v);
low[u]=min(low[u],low[v]);
if(low[v]==dfn[u]){
adde(u,++tot);int x;
do{x=stk[top--];adde(tot,x);}while(x!=v);
}
else if(low[v]>dfn[u])adde(u,v),top--;
}
else low[u]=min(low[u],dfn[v]);
}
}
#define P vector<int>
const int mod=998244353;
int wn[N<<1],wl;
int f[N][3];
int ksm(int x,int y)
{
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
y>>=1;x=1ll*x*x%mod;
}
return ret;
}
void init(int n)
{
wl=wn[0]=1;
while(wl<=n)wl<<=1;
wn[1]=ksm(3,(mod-1)/wl);
for(int i=2;i<=wl;i++)wn[i]=1ll*wn[i-1]*wn[1]%mod;
}
int rev[N];
void NTT(P &a,int len,int flg)
{
int i,j,k,x,y;
for(i=0;i<len;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(i=1,x=(wl>>1);i<len;i<<=1,x>>=1){
for(j=0;j<len;j+=(i<<1)){
for(k=j,y=0;k<i+j;k++,y+=x){
int tmp=1ll*a[i+k]*wn[flg==1?y:wl-y]%mod;
a[i+k]=(a[k]+mod-tmp)%mod;
a[k]=(a[k]+tmp)%mod;
}
}
}
if(flg==-1)for(i=0,x=ksm(len,mod-2);i<len;i++)
a[i]=1ll*a[i]*x%mod;
}
P mul(P a,P b)
{
int ret=a.size()+b.size()-1,len=1;
while(len<ret)len<<=1;a.resize(len);b.resize(len);
for(int i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|(i&1?len>>1:0);
NTT(a,len,1);NTT(b,len,1);
for(int i=0;i<len;i++)a[i]=1ll*a[i]*b[i]%mod;
NTT(a,len,-1);a.resize(ret);
return a;
}
P tmp[N];
int n,m,tcnt;
P solve(int l,int r)
{
if(l==r)return tmp[l];
int mid=(l+r)>>1;
return mul(solve(l,mid),solve(mid+1,r));
}
void DP(int u)
{
for(int v,p=fir[u];p;p=nxt[p])DP(to[p]);
if(u<=n){
if(!fir[u]){
f[u][0]=1;f[u][1]=(val[u]>=1);f[u][2]=(val[u]>=2);
return;
}
tcnt=0;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];
tmp[++tcnt].clear();
if(v>n)tmp[tcnt].push_back(f[v][2]);
tmp[tcnt].push_back(f[v][1]);
tmp[tcnt].push_back(f[v][0]);
}
P ret=solve(1,tcnt);
ret.resize(val[u]+1);
for(int i=1;i<=val[u];i++)
ret[i]=(ret[i]+ret[i-1])%mod;
f[u][0]=ret[val[u]];
if(val[u]>=1)f[u][1]=ret[val[u]-1];
if(val[u]>=2)f[u][2]=ret[val[u]-2];
}
else{
//f[u][0/1/2]
int g0,g1,t0,t1;
for(int k=0;k<=1;k++){
g0=!k;g1=k;
for(int v,p=fir[u];p;p=nxt[p]){
v=to[p];
t0=(1ll*g0*f[v][1]+1ll*g1*f[v][2])%mod;
t1=(1ll*g0*f[v][0]+1ll*g1*f[v][1])%mod;
g0=t0;g1=t1;
}
f[u][k+1]=(f[u][k+1]+g0)%mod;
f[u][k]=(f[u][k]+g1)%mod;
}
}
}
int main()
{
int i,u,v;
n=gi();m=gi();
init(2*n);
for(i=1;i<=m;i++){
u=gi();v=gi();
G[u].push_back(v);id[u].push_back(i);
G[v].push_back(u);id[v].push_back(i);
d[u]++;d[v]++;
}
for(i=1;i<=n;i++)val[i]=min(gi(),d[i]);
tot=n;for(i=1;i<=n;i++)if(!dfn[i])dfs(i);
DP(1);
printf("%d\n",f[1][0]);
}