LOJ#6496. 「雅礼集训 2018 Day1」仙人掌 题解

题目链接

这个题训练的时候做了……然后……我圆方树对方点儿子排序的部分写错了……

建出圆方树,然后考虑DP.

对于每个方点x,我们需要知道儿子节点(圆点)还剩下度数为0/1/2的方案数,用来DP。

对于每个圆点x,我们需要知道儿子节点会用掉x的多少度数以及对应的方案数,不难想到把它们写成一个多项式,卷积起来即可得到。这个点还剩下度数为0/1/2的方案数。

观察到不管是圆点还是方点,会用掉父亲节点的度数一定 \(\leq 2\) ,所以直接分治FFT,复杂度不超过 \(\Theta(n\log^2 n)\)

代码:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int P = 998244353,N = 100050,V = N<<1,NN = 524288;
inline void upd(int &x,int v){ x = (x+v>=P)?(x+v-P):(x+v); }
inline int power(int x,int y){ static int r; r = 1; while (y){ if (y&1) r = (LL)r * x % P; x = (LL)x * x % P; y >>= 1; } return r; }
int rt[30],irt[30],R[NN],inv[NN+5];
inline int getR(int n){
	static int Lim,l; Lim = 1,l = 0; while (Lim <= n) Lim <<= 1,++l;
	for (int i = 0; i < Lim; ++i) R[i] = (R[i>>1]>>1)|((i&1)<<l-1);
	return Lim;
}
inline void NTT(int *A,int n){
	register int i,j,k,l,x,w,w0;
	for (i = 0; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]);
	for (i = l = 1; i < n; i <<= 1,++l)
	for (j = 0,w0 = rt[l]; j < n; j += i << 1)
	for (k = j,w = 1; k < i+j; ++k,w = (LL)w * w0 % P)
		x = (LL)w * A[k+i] % P,A[k+i] = (A[k]<x) ? (A[k]+P-x) : (A[k]-x),A[k] = (x+A[k]>=P)?(x+A[k]-P):(x+A[k]);
}
inline void iNTT(int *A,int n){
	register int i,j,k,l,x,w,w0;
	for (i = 0; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]);
	for (i = l = 1; i < n; i <<= 1,++l)
	for (j = 0,w0 = irt[l]; j < n; j += i << 1)
	for (k = j,w = 1; k < i+j; ++k,w = (LL)w * w0 % P)
		x = (LL)w * A[k+i] % P,A[k+i] = (A[k]<x) ? (A[k]+P-x) : (A[k]-x),A[k] = (x+A[k]>=P)?(x+A[k]-P):(x+A[k]);
	for (i = 0,w = inv[n]; i < n; ++i) A[i] = (LL)A[i] * w % P;
}
typedef vector<int> Frac;
void Mulf(Frac &F,Frac &G,Frac &H){ // H = F * G
	static int A[NN],B[NN],n,m,i,Lim;
	n = F.size(),m = G.size();
	if (n <= 30 && m <= 30){
		H.resize(n+m-1); for (i = 0; i < n+m-1; ++i) H[i] = 0;
		for (i = 0; i < n; ++i) for (int j = 0; j < m; ++j) upd(H[i+j],(LL)F[i] * G[j] % P);
		return;
	}
	Lim = getR(n+m-2),memset(A,0,Lim<<2),memset(B,0,Lim<<2);
	for (i = 0; i < n; ++i) A[i] = F[i]; for (i = 0; i < m; ++i) B[i] = G[i];
	NTT(A,Lim); NTT(B,Lim); for (i = 0; i < Lim; ++i) A[i] = (LL)A[i] * B[i] % P; iNTT(A,Lim);
	H.resize(n+m-1); for (i = 0; i < n+m-1; ++i) H[i] = A[i]; return;
}
Frac F[V<<1]; int cntid,lim;
int Work(int l,int r){
	if (l == r) return l;
	int mid = l+r>>1,L = Work(l,mid),R = Work(mid+1,r),o = ++cntid;
	Mulf(F[L],F[R],F[o]); if (F[o].size() > lim+1) F[o].resize(lim+1);
	return o;
}
struct Mat{ int a00,a01,a10,a11; Mat(int v00 = 0,int v01 = 0,int v10 = 0,int v11 = 0){ a00 = v00,a01 = v01,a10 = v10,a11 = v11; } };
inline Mat operator * (Mat A,Mat B){
	return Mat(((LL)A.a01 * B.a00 + (LL)A.a00 * B.a10) % P,
			((LL)A.a01 * B.a01 + (LL)A.a00 * B.a11) % P,
			((LL)A.a11 * B.a00 + (LL)A.a10 * B.a10) % P,
			((LL)A.a11 * B.a01 + (LL)A.a10 * B.a11) % P);
}
int n,cntv,a[V]; Frac dp[V]; vector<int>Gr[V];
bool vis[V],dfsed[V],used[V]; int m,ex[V],ey[V],fa[V],fae[V],dpt[V],id[V];
inline void dfs1(int x){
	vis[x] = 1,dpt[x] = dpt[fa[x]] + 1;
	for (int i = 0,e,y; i < Gr[x].size(); ++i){
		e = Gr[x][i],y = ex[e] + ey[e] - x;
		if (!vis[y]) dfsed[e] = 1,fa[y] = x,fae[y] = e,dfs1(y);
	}
}
vector<int>G[V];
inline void adde(int x,int y){ G[x].push_back(y),G[y].push_back(x); }
inline void make_circle(int x,int y){
	if (dpt[x] > dpt[y]) swap(x,y); ++cntv; adde(cntv,y);
	id[y] = 1; while (y ^ x) used[fae[y]] = 1,id[fa[y]] = id[y] + 1,y = fa[y],adde(cntv,y);
}
inline bool cmp(int x,int y){ return id[x] < id[y]; }
int f[V][4],g[V][4];
inline void dfs2(int x){
	vector<int>ch;
	for (int i = 0,y; i < G[x].size(); ++i) if ((y=G[x][i])^fa[x]){ fa[y] = x,dfs2(y); if (x > n) ch.push_back(y); }
	if (x > n){
		for (int i = 0,y; i < ch.size(); ++i){
			y = ch[i],id[y] = dpt[y] - dpt[fa[x]]; if (id[y] < 0) id[y] += 1000000;
		}
		sort(ch.begin(),ch.end(),cmp);
		Mat T(0,1,1,0);
		for (int i = 0,y; i < ch.size(); ++i)
			y = ch[i],T = T * Mat(f[y][0],f[y][1],f[y][1],f[y][2]);
		g[x][2] = T.a00,g[x][1] = (T.a01 + T.a10) % P,g[x][0] = T.a11;
		return;
	}
	int m = 0;
	for (int i = 0,y; i < G[x].size(); ++i) if ((y=G[x][i])^fa[x]){
		++m; if (g[y][2]) F[m].resize(3); else if (g[y][1]) F[m].resize(2); else if (g[y][0]) F[m].resize(1); else F[m].resize(0);
		for (int j = 0; j < F[m].size(); ++j) F[m][j] = g[y][j];
	}
	if (m) lim = a[x],cntid = m,dp[x] = F[Work(1,m)]; else dp[x].resize(1),dp[x][0] = 1;
	for (int i = 0; i <= a[x] && i < dp[x].size(); ++i){
		if (i <= a[x]) upd(f[x][0],dp[x][i]);
		if (i <= a[x]-1) upd(f[x][1],dp[x][i]);
		if (i <= a[x]-2) upd(f[x][2],dp[x][i]);
	}
	g[x][1] = f[x][0],g[x][0] = f[x][1];
}
int main(){
	int i,j;
	for (i = 1,j = 2; i <= 25; ++i,j <<= 1) rt[i] = power(3,(P-1)/j),irt[i] = power(rt[i],P-2);
	for (inv[0] = inv[1] = 1,i = 2; i <= NN; ++i) inv[i] = (LL)(P-P/i) * inv[P%i] % P;
	ios::sync_with_stdio(0);
	cin >> n >> m; cntv = n;
	for (i = 1; i <= m; ++i) cin >> ex[i] >> ey[i],++a[ex[i]],++a[ey[i]],Gr[ex[i]].push_back(i),Gr[ey[i]].push_back(i);
	for (i = 1; i <= n; ++i) cin >> j,a[i] = min(a[i],j);
	dfs1(1);
	for (i = 1; i <= m; ++i) if (!dfsed[i]) make_circle(ex[i],ey[i]);
	for (i = 1; i <= m; ++i) if (dfsed[i] && !used[i]) adde(ex[i],ey[i]);
	dfs2(1);
	cout << f[1][0] << '\n';
	return 0;
}
posted @ 2020-09-01 23:22  srf  阅读(263)  评论(0编辑  收藏  举报