解题:WC 2018 州区划分

题面

WC之前写的,补一补,但是基本就是学新知识了

首先可以枚举子集$3^n$转移,优化是额外记录每个集合选取的个数,然后按照选取个数从小到大转移。转移的时候先FWT成“点值”转移完了IFWT回去乘逆元

沙茶博主也不知道为什么这样就是对的,放个没看懂的yww大佬的博客

 1 // luogu-judger-enable-o2
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=22,M=(1<<N)+N,K=110,mod=998244353;
 7 int mat[N][N],val[N],aset[N],deg[N],inv[N*K];
 8 int sum[M],gain[N][M],dp[N][M],bit[M];
 9 int n,m,p,t1,t2,all,lth;
10 void Mod(int &x,int y)
11 {
12     x+=y;
13     if(x<0) x+=mod;
14     if(x>=mod) x-=mod;
15 }
16 int S(int x)
17 {
18     return 1<<(x-1);
19 }
20 int Val(int x)
21 {
22     return p?(p==1?x:1ll*x*x%mod):1;
23 }
24 int Finda(int x)
25 {
26     return aset[x]==x?x:aset[x]=Finda(aset[x]);
27 }
28 int Calc(int sta)
29 {
30     register int i,j;
31     int abe=0,nde=0;
32     for(i=1;i<=n;deg[i]=0,i++)
33         if(sta&S(i)) aset[i]=i;
34     for(i=1;i<=n;i++)
35         if(sta&S(i))
36         {
37             nde=i,sum[sta]+=val[i];
38             for(j=1;j<=n;j++)
39                 if((sta&S(j))&&mat[i][j]) 
40                     deg[i]++,aset[Finda(j)]=Finda(i);
41             if(deg[i]%2) abe=1;
42         }
43     int anc=Finda(nde);
44     for(i=1;i<=n;i++) 
45         if(sta&S(i)) abe|=Finda(i)!=anc; 
46     return abe*Val(sum[sta]);
47 }
48 void Trans(int *arr,int len,int typ)
49 {
50     register int i,j,k;
51     for(i=2;i<=len;i<<=1)
52     {
53         int lth=i>>1;
54         for(j=0;j<len;j+=i)
55             for(k=j;k<j+lth;k++)
56                 Mod(arr[k+lth],typ*arr[k]);
57     }
58 }
59 void Pre()
60 {
61     register int i;
62     scanf("%d%d%d",&n,&m,&p),lth=1<<n,all=lth-1;
63     for(i=1;i<=m;i++)
64     {
65         scanf("%d%d",&t1,&t2);
66         mat[t1][t2]=mat[t2][t1]=true;
67     }
68     for(i=1;i<=n;i++) scanf("%d",&val[i]);
69     dp[0][0]=1,inv[1]=1,Trans(dp[0],lth,1);
70     for(i=2;i<=2100;i++)
71         inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
72     for(i=0;i<=all;i++)
73         bit[i]=bit[i>>1]+(i&1),gain[bit[i]][i]=Calc(i);
74     for(i=0;i<=n;i++) Trans(gain[i],lth,1);
75 }
76 int main()
77 {
78     Pre();
79     register int i,j,k;
80     for(i=1;i<=n;i++)
81     {
82         for(j=0;j<=i;j++)
83             for(k=0;k<=all;k++)
84                 Mod(dp[i][k],1ll*dp[j][k]*gain[i-j][k]%mod);
85         Trans(dp[i],lth,-1);
86         for(int j=0;j<=all;j++)
87             dp[i][j]=(bit[j]==i)?1ll*dp[i][j]*Val(inv[sum[j]])%mod:0;
88         if(i!=n) Trans(dp[i],lth,1);
89     }
90     printf("%d",dp[n][all]);
91     return 0;
92 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/10414949.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值