正解:子集和变换。
考场上只会暴力和$p=0$的情况,还只会$O(2^{n}*n^{3})$的。
然而这题题面出锅,导致考场上一直在卡裸暴力,后面的部分分没写了。。听$laofu$说$O(2^{n}*n^{3})$可以过。。
所以直接讲正解。。
我们假设每个城市可以在两个不同集合,那么可以把子集卷积变成或卷积。
我们只要记下当前总共有多少个点,于是考虑设$f[i][S]$表示$i$个点,集合为$S$的方案数。
最后的$f[n][all]$就是答案,显然这个状态中的每个城市只会出现一次。
那么$f[i][S]=\sum f[j][T]*(\frac{sum[A]}{sum[S]})^{p}$,其中$A|T=S$,且$A$是一个合法集合。
可以把分母移项到左边,然后我们可以设$g[i][S]$表示如果$S$是一个合法集合,且$S$的位数为$i$,那么$g[i][S]=sum[S]^{p}$,否则为$0$。
那么$f[i][S]*sum[S]^{p}=\sum f[j][T]*g[i-j][A]$。注意到这个式子可以直接$FMT$以后点乘,再$IFMT$回来以后作除法得到,总复杂度为$O(2^{n}*n^{2})$。
感觉这道题其实并没有那么难,但是考场上被题面以及固定的套路给局限住了,所以并没有想到可以交换$dp$的两维状态从而优化复杂度。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define rhl (998244353) 6 #define N (1<<21|1) 7 8 using namespace std; 9 10 int fa[25],g[25],w[N],cnt[N],can[N],inv[10005],n,m,p,all; 11 int f[22][N],h[22][N]; 12 13 il int gi(){ 14 RG int x=0,q=1; RG char ch=getchar(); 15 while ((ch<‘0‘ || ch>‘9‘) && ch!=‘-‘) ch=getchar(); 16 if (ch==‘-‘) q=-1,ch=getchar(); 17 while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 18 return q*x; 19 } 20 21 il int qpow(RG int a,RG int b){ 22 if (!b) return 1; 23 if (b==1) return a; 24 return 1LL*a*a%rhl; 25 } 26 27 il void fmt(int *a){ 28 for (RG int i=1;i<all;i<<=1) 29 for (RG int j=0;j<all;++j){ 30 if (j&i) a[j]+=a[j^i]; 31 if (a[j]>=rhl) a[j]-=rhl; 32 } 33 return; 34 } 35 36 il void ifmt(int *a){ 37 for (RG int i=1;i<all;i<<=1) 38 for (RG int j=0;j<all;++j){ 39 if (j&i) a[j]-=a[j^i]; 40 if (a[j]<0) a[j]+=rhl; 41 } 42 return; 43 } 44 45 il int find(RG int x){ 46 return fa[x]==x ? x : fa[x]=find(fa[x]); 47 } 48 49 int main(){ 50 #ifndef ONLINE_JUDGE 51 freopen("walk.in","r",stdin); 52 freopen("walk.out","w",stdout); 53 #endif 54 n=gi(),m=gi(),p=gi(),all=1<<n,inv[1]=1; 55 for (RG int i=1,u,v;i<=m;++i) 56 u=gi()-1,v=gi()-1,g[u]|=1<<v,g[v]|=1<<u; 57 for (RG int i=2;i<=3000;++i) 58 inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl; 59 for (RG int i=0;i<n;++i) w[1<<i]=gi(); fmt(w); 60 for (RG int i=1;i<all;++i) cnt[i]=cnt[i>>1]+(i&1); 61 for (RG int i=1,fg,lst;i<all;++i){ 62 fg=0,lst=-1; 63 for (RG int j=0;j<n;++j) fa[j]=j; 64 for (RG int j=0;j<n;++j){ 65 if (!(i>>j&1)) continue; 66 for (RG int k=0,x,y;k<n;++k){ 67 if (!(i>>k&1) || !(g[j]>>k&1)) continue; 68 x=find(j),y=find(k); if (x!=y) fa[x]=y; 69 } 70 } 71 for (RG int j=0;j<n;++j){ 72 if (i>>j&1){ 73 if (lst==-1) lst=find(j); 74 else if (lst!=find(j)){ fg=1; break; } 75 } 76 if ((i>>j&1) && (cnt[g[j]&i]&1)){ fg=1; break; } 77 } 78 can[i]=fg; 79 } 80 for (RG int i=0;i<all;++i) if (can[i]) h[cnt[i]][i]=qpow(w[i],p); 81 for (RG int i=1;i<=n;++i) fmt(h[i]); f[0][0]=1,fmt(f[0]); 82 for (RG int i=1;i<=n;++i){ 83 int *F=f[i]; 84 for (RG int j=0;j<i;++j){ 85 int *a=f[j],*b=h[i-j]; 86 for (RG int s=0;s<all;++s) 87 F[s]=(1LL*a[s]*b[s]+F[s])%rhl; 88 } 89 ifmt(F); 90 for (RG int s=0;s<all;++s) 91 F[s]=i==cnt[s]?1LL*F[s]*qpow(inv[w[s]],p)%rhl:0; 92 if (i^n) fmt(F); 93 } 94 cout<<f[n][all-1]; return 0; 95 }
原文地址:https://www.cnblogs.com/wfj2048/p/8443080.html
时间: 2024-11-06 03:36:35