FZOJ4056. 「2019-02-24联考」高铁(railway)(tarjan,广义圆方树,点分治,NTT)

3 篇文章 0 订阅
2 篇文章 0 订阅

题目大意:
给一个 N N N 个点 M M M 条边的无向图
问有多少个无序三元组 ( C , x , y ) (C,x,y) (C,x,y)
满足 C C C 中的元素在 x → y x\rightarrow y xy 的所有简单路径中
其中 C C C 是一个集合 x , y ∈ C x,y\in C x,yC
n ≤ 1 0 5 , m ≤ 5 ∗ 1 0 5 n\le 10^5,m\le5*10^5 n105,m5105
部分分有树,仙人掌,随机树等等


考虑树的部分分,点分治 N T T NTT NTT 求出所有长度为 k k k 的路径后可以把贡献的式子拆开后再用 N T T NTT NTT 求出所有答案
考虑一般图(仙人掌)
其实一版图和仙人掌处理方法差不多

显然路径经过一个点双那么点双内的点必然被全部经过
除了到一个点双的端点的时候

直接上广义圆方树就行了,方点的权值为点双内的点数 − 2 -2 2,圆点的权值为 1 1 1
把圆点直接算贡献即可
这时候点分治 N T T NTT NTT 会多一些细节,注意一下细节就好了

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define file(x) memset(x,0,sizeof(x))
#define pb push_back
#define SZ(x) ((int)(x.size()))
namespace io {
	const int SIZE = (1 << 21) + 1;
	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
	// getchar
	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
	// print the remaining part
	inline void flush () {
		fwrite (obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	// putchar
	inline void putc (char x) {
		*oS ++ = x;
		if (oS == oT) flush ();
	}
	// input a signed integer
	template <class I>
	inline void gi (I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
		for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
	}
	// print a signed integer
	template <class I>
	inline void print (I &x) {
		if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
		while (x) qu[++ qr] = x % 10 + '0',  x /= 10;
		while (qr) putc (qu[qr --]);
	}
	//no need to call flush at the end manually!
	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int p = 998244353;
inline int calc(int a,int b){return (a+b)%p;}
inline int del(int a,int b){return (a-b+p)%p;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int max(int a,int b){return a>b?a:b;}
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now = 1ll*now*a%p; return now;}
const int inv2 = ksm(2,p-2);
int n,m,tp;
int linkk[101000],t;
struct node{int n,y;}e[1001000];
int A[801000],B[801000],v[201000];
int dfn[101000],low[101000],tot,id;
vector<int>in[101000],G[201000];
stack<int>st; 
namespace NTT{
    const int g = 3;
	int r[1601000],w[1601000];
	void ntt(int *a,int f,int flen){
		w[0] = 1; rep(i,0,flen-1) r[i] = (r[i>>1]>>1) | ((i&1)?flen/2:0);
		rep(i,0,flen-1) if(i < r[i]) swap(a[i],a[r[i]]);
		for(int len = 2;len <= flen;len <<= 1){
			int wn = ksm(g,(p-1)/len); if(f == -1) wn = ksm(wn,p-2);
			rep(i,1,len-1) w[i] = mul(w[i-1],wn);
			for(int st = 0;st < flen;st += len)
			    rep(i,0,(len>>1)-1){
			    	int x = a[st+i],y = mul(a[st+(len>>1)+i],w[i]);
			    	a[st+i] = calc(x,y);    a[st+(len>>1)+i] = del(x,y);
				}
		}
		if(f == -1){int inv = ksm(flen,p-2);rep(i,0,flen-1) a[i] = mul(a[i],inv);}
	}
	void Mul(int *a,int n,int *b,int m){
		int flen = 1; while(flen < n+m-1) flen <<= 1;
		rep(i,n,flen-1) a[i] = 0; rep(i,m,flen-1) b[i] = 0;
		ntt(a,1,flen); ntt(b,1,flen); rep(i,0,flen-1) a[i] = mul(a[i],b[i]);
		ntt(a,-1,flen);
	}
	void Mul2(int *a,int n){
		int flen = 1; while(flen < 2*n-1) flen <<= 1;
		rep(i,n,flen-1) a[i] = 0; ntt(a,1,flen); rep(i,0,flen-1) a[i] = mul(a[i],a[i]); ntt(a,-1,flen);
	}
}using namespace NTT;
namespace work{
	int f[201000],sz[201000],rt,now_size;
	int ans[201000],a[801000],b[801000],mx,tot_mx;
	bool inq[201000];
	void get_rt(int x,int fa){
		f[x] = 0; sz[x] = v[x];
		rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]] && G[x][i] != fa){
			get_rt(G[x][i],x); f[x] = max(f[x],sz[G[x][i]]); sz[x] += sz[G[x][i]];
		}
		f[x] = max(f[x],now_size-sz[x]); if(f[x] < f[rt]) rt = x;
	}
	void get_dis(int x,int fa,int dep){
	    if(x <= n) a[dep]++; mx=max(mx,dep); 
	    rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]] && G[x][i] != fa) get_dis(G[x][i],x,dep+v[G[x][i]]);
	}
	void tr_calc(int x,int gg,int f){
		mx = 0;get_dis(x,0,0); f += (gg==1?1:2)*(x<=n?1:v[x]);
		NTT::Mul2(a,mx+1); 
		rep(i,0,2*mx) ans[i+f-1] = calc(ans[i+f-1],mul(gg,a[i])); 
		rep(i,0,2*mx) a[i] = 0; tot_mx = max(tot_mx,2*mx+f-1);
	}
	void solve(int x){
		inq[x] = true; tr_calc(rt,1,0);
		rep(i,0,SZ(G[x])-1) if(!inq[G[x][i]])
		    tr_calc(G[x][i],-1,(x<=n?1:v[x])) , now_size = sz[G[x][i]] , rt = 0 , 
		    get_rt(G[x][i],x) , solve(rt);
    }
	void doit(){
		rt = 0;f[0] = 2*now_size;
		get_rt(1,0);solve(rt);
	}
}using namespace work;
namespace final{
	int fac[301000],inv[301000];
    inline int C(int x,int y){return (y<0||y>x)?0:mul(fac[x],mul(inv[y],inv[x-y]));}  
	void pre(){
		fac[0] = inv[0] = 1;
		rep(i,1,300000) fac[i] = 1ll*fac[i-1]*i%p,inv[i] = ksm(fac[i],p-2);
	}
	void solve1(){
		rep(i,1,work::tot_mx) work::ans[i] = mul(work::ans[i],inv2);
		//ans[i]->i+1¸ö
	    rep(i,0,n-2) A[i] = mul(work::ans[n-i-1],fac[n-i-2]);
		rep(i,0,n-1) B[i] = inv[i];
		Mul(A,n,B,n);
    	rep(i,2,n) printf("%d\n",mul(A[n-i],inv[i-2]));
	}
}
void insert(int x,int y){
	e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;
	e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;
}
void tarjan(int x,int fa){
	dfn[x] = low[x] = ++tot; st.push(x);
	for(int i = linkk[x];i;i = e[i].n) if(i != fa) {
		int y = e[i].y;
		if(!dfn[y]) {
			tarjan( y , i );
			low[x] = min(low[x],low[y]);
			if(low[y] >= dfn[x]) {
				id++;
				while(st.top() != y) G[id].pb(st.top()),G[st.top()].pb(id),st.pop(),v[id]++;
				G[id].pb(st.top()); G[st.top()].pb(id); st.pop(); G[x].pb(id); G[id].pb(x); 
				work::now_size += v[id];
			}
		}else low[x] = min(low[x],dfn[y]); 
	}
}
int main(){
	freopen("railway.in","r",stdin);
	freopen("railway.out","w",stdout);
	gi(n); gi(m);int x,y;
	rep(i,1,m) gi(x),gi(y),insert(x,y);
	rep(i,1,n) v[i] = 1; id = n; work::now_size = n;
	tarjan(1,0);
	work::doit();  final::pre();
	final::solve1();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值