JZOJ 5700. 【gdoi2018 day1】小学生图论题(graph)
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,TS,T ,邊都是從 SS 連向 TT 的。
那么把點集對應到原競賽圖中,點可以分成兩個點集 S,TS,T ,且邊都是從 SS 連向 TT 的,就說明縮完點后對應一個分割。
我們只要求出期望的分割數即可,強連通分量個數就是分割數+1。
答案可以寫成:
1+∑S,T∏x∈S,y∈TPx,y1+∑S,T∏x∈S,y∈TPx,y由題知,邊的概率 Px,y∈{0,12,1}Px,y∈{0,12,1} 。
于是當 m=0m=0 時,我們很容易得到答案:
Ans=1+∑i=1n?112i?(n?i)?CinAns=1+∑i=1n?112i?(n?i)?Cni其中 ii 枚舉的是 SS 的大小,此時 SS 到 TT 的邊方向確定。
如果 m≠0m≠0 呢?考慮題目給出的每一條鏈,顯然點的編號是與答案無關的。
鏈可以分成前后兩段,前一段屬于 SS ,后一段屬于 TT 。
對于一個長度為 kk 的鏈,我們嘗試構造一個 kk 階多項式 G(x)G(x) ,xixi 項
如果有的點不在題目給出的鏈中,那么就各自當成獨立的一個長度為 11 的鏈。
如果鏈上的點同在 SS 集或 TT 集中,那么使該項系數為 11 ,即 00 次項和 kk 次項系數為 11 。
否則的話,肯定有一條邊的方向已經確定了,于是概率應少乘一個 1212 ,
那么第 11 次項到第 k?1k?1 次項的系數就是 22 。
于是對于每條鏈,我們都構造出了一個多項式:G(x)=1+2x+2x2+???+2xk?1+xkG(x)=1+2x+2x2+···+2xk?1+xk
用分治NTT將所有多項式都乘起來,時間復雜度 O(N?log2N)O(Nlog2N) 。
令所有 G(x)G(x) 乘起來的多項式為 F(x)F(x) ,則最后答案即為:
Ans=1+∑i=1n?112i(n?i)?F(x)[xi]Ans=1+∑i=1n?112i(n?i)?F(x)[xi]
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; }總結
以上是生活随笔為你收集整理的JZOJ 5700. 【gdoi2018 day1】小学生图论题(graph)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5703. 【gdoi2018
- 下一篇: BZOJ 4802: 欧拉函数(大数因数