Description
Input
Output
Sample Input
Sample Input1
10 2
2 1 3
3 7 8 9
Sample Input2
3 0
Sample Output
Sample Output1
462789157
Sample Output2
499122179
Data Constraint
Solution
省选前做过一道极其类似的原题,是MYY出的,但是我当时没搞懂,好亏啊!!!
我们将强连通分量缩成点后,就会形成类似一条链、前往后有连边的拓扑图。如下图:
链上的一条边就是一个分割,分成前后两个点集 S,T S , T ,边都是从 S S 连向 的。
那么把点集对应到原竞赛图中,点可以分成两个点集 S,T S , T ,且边都是从 S S 连向 的,就说明缩完点后对应一个分割。
我们只要求出期望的分割数即可,强连通分量个数就是分割数+1。
答案可以写成:
1+∑S,T∏x∈S,y∈TPx,y 1 + ∑ S , T ∏ x ∈ S , y ∈ T P x , y由题知,边的概率 Px,y∈{0,12,1} P x , y ∈ { 0 , 1 2 , 1 } 。
于是当 m=0 m = 0 时,我们很容易得到答案:
Ans=1+∑i=1n−112i∗(n−i)∗Cin A n s = 1 + ∑ i = 1 n − 1 1 2 i ∗ ( n − i ) ∗ C n i其中 i i 枚举的是 的大小,此时 S S 到 的边方向确定。
如果 m≠0 m ≠ 0 呢?考虑题目给出的每一条链,显然点的编号是与答案无关的。
链可以分成前后两段,前一段属于 S S ,后一段属于 。
对于一个长度为 k k 的链,我们尝试构造一个 阶多项式 G(x) G ( x ) , xi x i 项
如果有的点不在题目给出的链中,那么就各自当成独立的一个长度为 1 1 的链。
如果链上的点同在 集或 T T 集中,那么使该项系数为 ,即 0 0 次项和 次项系数为 1 1 。
否则的话,肯定有一条边的方向已经确定了,于是概率应少乘一个 ,
那么第 1 1 次项到第 次项的系数就是 2 2 。
于是对于每条链,我们都构造出了一个多项式:
用分治NTT将所有多项式都乘起来,时间复杂度 O(N log2N) O ( N l o g 2 N ) 。
令所有 G(x) G ( x ) 乘起来的多项式为 F(x) F ( x ) ,则最后答案即为:
Ans=1+∑i=1n−112i(n−i)∗F(x)[xi] A n s = 1 + ∑ i = 1 n − 1 1 2 i ( n − i ) ∗ F ( x ) [ x i ]
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=1e5+5,G=3,mo=998244353;
int a[N<<1],beg[N],end[N];
int w[N<<2],rev[N<<2],b[N<<2],c[N<<2];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int ksm(int x,LL y)
{
int s=1;
while(y)
{
if(y&1) s=(LL)s*x%mo;
x=(LL)x*x%mo;
y>>=1;
}
return s;
}
inline void NTT(int *y,int n,int ff)
{
for(int i=0;i<n;i++)
if(i<rev[i]) swap(y[i],y[rev[i]]);
for(int m=2;m<=n;m<<=1)
{
int h=m>>1;
for(int i=0;i<h;i++)
{
int wn=ff==-1?w[n-i*(n/m)]:w[i*(n/m)];
for(int j=i;j<n;j+=m)
{
int u=y[j],v=(LL)wn*y[j+h]%mo;
y[j]=(u+v)%mo;
y[j+h]=(u-v+mo)%mo;
}
}
}
if(ff==-1)
{
int inv=ksm(n,mo-2);
for(int i=0;i<n;i++) y[i]=(LL)y[i]*inv%mo;
}
}
void solve(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
solve(l,mid);
solve(mid+1,r);
int ln=end[l]-beg[l]+1,lm=end[mid+1]-beg[mid+1]+1;
int len=1,ll=0;
while(len<=ln+lm+1) len<<=1,ll++;
for(int i=0;i<len;i++) rev[i]=rev[i>>1]>>1|(i&1)<<ll-1;
for(int i=0;i<ln;i++) b[i]=a[beg[l]+i];
for(int i=ln;i<=len;i++) b[i]=0;
for(int i=0;i<lm;i++) c[i]=a[beg[mid+1]+i];
for(int i=lm;i<=len;i++) c[i]=0;
int num=ksm(G,(mo-1)/len);
for(int i=w[0]=1;i<=len;i++) w[i]=(LL)w[i-1]*num%mo;
NTT(b,len,1),NTT(c,len,1);
for(int i=0;i<len;i++) b[i]=(LL)b[i]*c[i]%mo;
NTT(b,len,-1);
while(len && !b[len-1]) len--;
end[l]=beg[l]+len-1;
for(int i=beg[l];i<=end[l];i++) a[i]=b[i-beg[l]];
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
int n=read(),m=read(),tot=0;
for(int i=1;i<=m;i++)
{
int k=read();
tot+=k;
beg[i]=end[i-1]+1;
end[i]=beg[i]+k;
a[beg[i]]=a[end[i]]=1;
for(int j=beg[i]+1;j<end[i];j++) a[j]=2;
while(k--) read();
}
for(int i=tot+1;i<=n;i++)
{
m++;
beg[m]=end[m-1]+1;
end[m]=beg[m]+1;
a[beg[m]]=a[end[m]]=1;
}
solve(1,m);
int ans=1;
for(int i=1;i<n;i++) ans=(ans+(LL)a[i+beg[1]]*ksm((mo+1)>>1,(LL)i*(n-i)))%mo;
printf("%d",ans);
return 0;
}