zoukankan      html  css  js  c++  java
  • LOJ#6496. 「雅礼集训 2018 Day1」仙人掌 题解

    题目链接

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

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

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

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

    观察到不管是圆点还是方点,会用掉父亲节点的度数一定 (leq 2) ,所以直接分治FFT,复杂度不超过 (Theta(nlog^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] << '
    ';
    	return 0;
    }
    
  • 相关阅读:
    jzoj 3176. 【GDOI2013模拟5】蜘蛛侠
    各种各样的根号算法 总结&刷题
    jzoj 3187. 【GDOI2013模拟8】的士
    jzoj 3188. 【GDOI2013模拟8】找数
    jzoj 4673. 【NOIP2016提高A组模拟7.20】LCS again
    jzoj 4672. 【NOIP2016提高A组模拟7.20】Graph Coloring
    markdown 模板2
    树莓派kali开启arp【arpspoof,urlsnarf】
    Java 图片处理——如何生成高清晰度而占有磁盘小的缩略图
    手把手教你生成二维码-google.zxing
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13599445.html
Copyright © 2011-2022 走看看