UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html

题目传送门 - UOJ#23

题意

  给定一个有 n 个节点的仙人掌(可能有重边)。

  对于所有的 $L(1\leq L\leq n-1)$ ,求出有多少不同的从节点 1 出发的包含 L 条边的简单路径。简单路径是指不重复经过任意一点。

  $n\leq 10^5$

题解

  首先我们把走一条边看作多项式 $x^1$ ,那么一条长度为 L 的路径就是其路径上的多项式的乘积。

  接下来称“环根”为距离节点 1 最近的那个节点。

  假设 $p_v$ 为点 v 对最终答案的贡献,那么 $p_v$ 是什么?分两种情况讨论:

    1. v 属于某个环且 v 不是该环的环根。那么显然这个环的环根(设为 u)更靠近 1 ,假设 v 到 u 的两条路径长度分别是 a 和 b ,那么 $p_v=p_u \times (x^a+x^b)$ 。

    2. 假设有一个点 u 不和 v 在同一个环中,且 u 距离 1 比 v 距离 1 更近,那么 $p_v=p_v\times x$ 。

  于是到此为止我们已经可以轻松的解决 50 分了。

  然而跳蚤国的疆域着实太大……

  考虑对于仙人掌进行点分治,然后对于当前连通块 $T$ ,我们找点分中心 $x$ 。

  怎么找点分中心?——类比找树的点分中心,"找一个结点,把和它相连的边都断了并且他在的每一个环上的边都要去掉(不去掉环上的其它结点)。这样找出连通块最大的最小作为重心。"

  定义连通块的根为当前连通块中在原图距离 1 最近的点。

  于是我们可以分治 FFT 求出点分中心 x 到 当前连通块的根 的多项式。

  结合点分治,总复杂度为 $O(n\log^3 n)$ ,可能可以通过此题。

UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

——VFleaKing

  对于当前点分中心 x ,如果我们先将比 x 靠近连通块根的那个子仙人掌处理了,那么,我们会发现我们在这时要算的多项式大部分已经算好了。这里只需要做的就是合并。可以发现,从 x 追根溯源得到的这些多项式有 $O(\log n)$ 个,而且他们的最高次项指数是和对应的连通块的 size 相关的,又由于对于这些连通块,我们做的是点分治,所以这些多项式的最高次项指数是大约 2 倍两倍增长的。所以现在求点分中心 x 到 当前连通块的根 的多项式,只需要 $T(n) = T(n/2) + O(n\log  n) = O(n\log n)$ 的时间复杂度。

  结合点分治,我们可以得到一个 $O(n\log ^2 n)$ 的优秀做法。

  当然,底层实现的时候还是有一些细节需要注意,这里就不展开赘述了(饶了我吧,懒得写了……)。

  由于博主人傻常数大,所以用时差点吃鸡了。

UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

  顺便赠送样例 2 的图,以及其圆方树。

UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

代码

#include <bits/stdc++.h>
int Debug=0;
using namespace std;
typedef long long LL;
LL read(){
LL x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
const int N=(1<<18)+9,mod=998244353;
int Log[N];
int Pow(int x,int y){
int ans=1;
for (;y;y>>=1,x=(LL)x*x%mod)
if (y&1)
ans=(LL)ans*x%mod;
return ans;
}
void Add(int &x,int y){
if ((x+=y)>=mod)
x-=mod;
}
int del(int x,int y){
return x-y<0?x-y+mod:x-y;
}
int rev[N],w[N],A[N],B[N];
void FFT(int a[],int n){
for (int i=0;i<n;i++)
if (rev[i]<i)
swap(a[i],a[rev[i]]);
for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
for (int i=0;i<n;i+=(d<<1))
for (int j=0;j<d;j++){
int tmp=(LL)w[j*t]*a[i+j+d]%mod;
a[i+j+d]=del(a[i+j],tmp);
Add(a[i+j],tmp);
}
}
vector <int> Mul(vector <int> a,vector <int> b){
static vector <int> ans;
int n=1,d=0;
for (;n<a.size()+b.size();n<<=1,d++);
w[0]=1,w[1]=Pow(3,(mod-1)/n);
for (int i=2;i<n;i++)
w[i]=(LL)w[i-1]*w[1]%mod;
for (int i=0;i<n;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(d-1));
for (int i=0;i<n;i++)
A[i]=B[i]=0;
for (int i=0;i<a.size();i++)
A[i]=a[i];
for (int i=0;i<b.size();i++)
B[i]=b[i];
FFT(A,n),FFT(B,n);
for (int i=0;i<n;i++)
A[i]=(LL)A[i]*B[i]%mod;
w[1]=Pow(w[1],mod-2);
for (int i=2;i<n;i++)
w[i]=(LL)w[i-1]*w[1]%mod;
FFT(A,n);
int inv=Pow(n,mod-2);
for (int i=0;i<n;i++)
A[i]=(LL)A[i]*inv%mod;
while (n>0&&!A[n-1])
n--;
ans.clear();
for (int i=0;i<n;i++)
ans.push_back(A[i]);
return ans;
}
struct poly{
vector <int> v;
poly(){}
poly(int x){
v.clear();
while (x>=(int)v.size())
v.push_back(0);
v[x]++;
}
void add_move(poly &p,int x){
int s=max(v.size(),p.v.size()+x);
while (v.size()<s)
v.push_back(0);
for (int i=0;i<p.v.size();i++)
Add(v[i+x],p.v[i]);
}
poly operator << (int x){
poly res=*this;
reverse(res.v.begin(),res.v.end());
while (x--)
res.v.push_back(0);
reverse(res.v.begin(),res.v.end());
return res;
}
void operator += (poly A){
while (v.size()<A.v.size())
v.push_back(0);
for (int i=0;i<A.v.size();i++)
Add(v[i],A.v[i]);
}
void operator *= (poly A){
v=Mul(v,A.v);
}
}C;
poly operator + (poly A,poly B){
C.v.clear();
for (int i=max(A.v.size(),B.v.size());i>0;i--)
C.v.push_back(0);
for (int i=0;i<A.v.size();i++)
Add(C.v[i],A.v[i]);
for (int i=0;i<B.v.size();i++)
Add(C.v[i],B.v[i]);
return C;
}
poly operator * (poly A,poly B){
C.v=Mul(A.v,B.v);
return C;
}
struct Gragh{
static const int M=N*4;
int cnt,y[M],nxt[M],fst[N];
void clear(){
cnt=1;
memset(fst,0,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
}
}g;
int n,m,k;
int fa[N];
int dfn[N],low[N],st[N],Time=0,st_top=0,vise[N*2];
vector <int> circle[N],T[N];
void T_add(int a,int b){
T[a].push_back(b);
T[b].push_back(a);
}
void Tarjan(int x){
dfn[x]=low[x]=++Time,st[++st_top]=x;
for (int i=g.fst[x];i;i=g.nxt[i]){
if (vise[i>>1])
continue;
vise[i>>1]=1;
int y=g.y[i];
if (!dfn[y]){
Tarjan(y);
low[x]=min(low[x],low[y]);
if (low[y]>dfn[x]){
T_add(x,y);
fa[y]=x;
st_top--;
}
else if (low[y]==dfn[x]){
k++;
T_add(x,k);
circle[k].clear();
circle[k].push_back(x);
while (st[st_top]!=x&&low[st[st_top]]==dfn[x]){
T_add(k,st[st_top]);
circle[k].push_back(st[st_top]);
fa[st[st_top]]=x;
st_top--;
}
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
int fad1[N],fad2[N];
int size[N],fimax[N],semax[N],vis[N],top[N];
int Tfa[N],Tdepth[N];
void dfsT(int x,int pre,int d){
Tfa[x]=pre,Tdepth[x]=d;
for (auto y : T[x])
if (y!=pre)
dfsT(y,x,d+1);
}
int Size,RT;
void get_size(int x,int pre){
size[x]=x<=n?1:0,fimax[x]=semax[x]=0;
for (auto y : T[x])
if (y!=pre&&!vis[y]){
get_size(y,x);
size[x]+=size[y];
if (size[y]>fimax[x])
semax[x]=fimax[x],fimax[x]=size[y];
else if (size[y]>semax[x])
semax[x]=size[y];
}
}
void get_root(int x,int pre){
if (x<=n){
fimax[x]=semax[x]=0;
int f=Tfa[x];
if (!vis[f]){
if (f<=n)
fimax[x]=Size-size[x];
else
fimax[x]=size[x]==fimax[f]?semax[f]:fimax[f];
}
}
else {
if (Size-size[x]>fimax[x])
semax[x]=fimax[x],fimax[x]=Size-size[x];
else if (Size-size[x]>semax[x])
semax[x]=Size-size[x];
}
for (auto y : T[x])
if (y!=pre&&!vis[y]){
if (x<=n)
fimax[x]=max(fimax[x],y<=n?size[y]:fimax[y]);
get_root(y,x);
}
if (x<=n)
if (!RT||fimax[RT]>fimax[x])
RT=x;
}
poly up[N],dn[N],po,potmp;
int d_solve=0;
void solve(int x){
get_size(x,0);
Size=size[x],RT=0;
get_root(x,0);
assert(RT!=0);
vis[x=RT]=++Time;
top[x]=x;
while (!vis[fa[top[x]]])
top[x]=fa[top[x]];
dn[x].v.push_back(0);
Add(dn[x].v[0],1);
for (auto y : T[x])
if (!vis[y])
if (y<=n){
solve(y);
if (Tdepth[y]>Tdepth[x])
dn[x].add_move(dn[y],1);
}
else {
vis[y]=vis[x];
for (auto z : T[y])
if (!vis[z]&&z!=x){
solve(z);
if (Tdepth[z]>Tdepth[y]){
dn[y].add_move(dn[z],fad1[z]);
dn[y].add_move(dn[z],fad2[z]);
}
}
if (Tdepth[y]>Tdepth[x])
dn[x].add_move(dn[y],0);
}
up[x].v.clear();
if (vis[fa[x]]>vis[x]){
int f=fa[x];
po.v.clear();
po=poly(0);
while (f!=top[x]){
if (top[f]==f){
if (Tfa[f]>n){
potmp.v.clear();
potmp=po;
po.v.clear();
po.add_move(potmp,fad1[f]);
po.add_move(potmp,fad2[f]);
}
else
po=po<<1;
f=fa[f];
}
else {
po*=up[f];
f=top[f];
}
}
if (Tfa[x]>n){
top[fa[x]]=top[x];
up[fa[x]]=po;
up[x].add_move(po,fad1[x]);
up[x].add_move(po,fad2[x]);
}
else
up[x].add_move(po,1);
}
else
up[x].v.push_back(1);
if (top[x]!=x)
dn[top[x]]+=dn[x]*up[x];
if (vis[Tfa[x]]>=vis[x]&&Tfa[x]>n)
dn[top[x]]+=dn[Tfa[x]]*up[fa[x]];
}
int main(){
Log[1]=0;
for (int i=2;i<N;i++)
Log[i]=Log[i>>1]+1;
n=k=read(),m=read();
g.clear();
for (int i=1;i<=m;i++){
int a=read(),b=read();
g.add(a,b);
g.add(b,a);
}
Tarjan(1);
for (int i=n+1;i<=k;i++){
int tmp=circle[i].size();
for (int j=1;j<tmp;j++){
fad1[circle[i][j]]=j;
fad2[circle[i][j]]=tmp-j;
}
}
dfsT(1,0,0);
vis[0]=1;
for (int i=1;i<=k;i++)
dn[i].v.clear();
Time=0;
solve(1);
while (dn[1].v.size()<n)
dn[1].v.push_back(0);
for (int i=1;i<n;i++)
printf("%d\n",dn[1].v[i]);
return 0;
}

  

上一篇:Android Studio 好用的设置


下一篇:KVM virsh常用命令篇