由于 NFLSOJ 题面上啥也没有就把题意贴这儿了(

没事儿,反正是上赛季的题,你们非六校学生看了就看了,况且看了你们也没地方交就是了

题意:

  • 给你一张 \(n\) 个点 \(m\) 条边的图 \(G=(V,E)\)。
  • 要求有多少张子图 \(G'=(V',E')\)​ 满足 \(G'\) 是连通二分图。
  • \(n\le 20,m\le\dfrac{n(n-1)}{2}\)
  • 答案对 \(998244353\) 取模。

输入样例 #1:

3 3
1 2
1 3
2 3

输出样例 #1:

9

众所周知,这场比赛现场六校就我一个人打(

首先考虑 \(n\le 17\)​ 怎么做,也就是本题的 80 分做法,注意到二分图计数这个东西是不太好直接求的,因为它涉及点集和边集两个决策对象,而这两个决策对象之间没有直接关联,因此考虑换个求解方式,我们将每个二分图与给二分图每个顶点进行黑白染色的方案看作一个整体,也就是二分染色图的数量,这样我们可以枚举染黑的点集 \(B\) 和染白的点集 \(W\),这样点集显然就是 \(B\cup W\),边集的限制就变为了不能存在一条连接两个 \(B\) 中节点的边,也不能存在一条连接两个 \(W\)​ 中节点的边,而 \(B\) 与 \(W\) 之间的边可以随意选择,因此假设 \(c\) 为 \(B\) 与 \(W\) 之间的边数,\(f_S\) 以 \(S\) 中点为点集的二分染色图的数量,那么有 \(f_{B\cup W}=2^c\)。

接下来考虑求出 \(f_S\) 之后怎样求解答案,注意到对于一个连通图而言,假如它是一个二分图,那么它对应的染色方法必然恰有 \(2\) 种,也就是说,假设以 \(S\) 为点集的连通二分图数量为 \(x\),那么以 \(S\) 为点集的连通二分染色图的数量就是 \(2x\),因此我们只需要求出 \(g_S\) 表示以 \(S\) 为点集的连通二分染色图数量即可求出答案,怎么求呢?其实做过这题的同学思考到这一步应该比较有感觉了,我们考虑再设 \(h_S\) 表示以 \(S\)​ 为点集的不连通二分染色图数量,那么显然 \(g_S+h_S=f_S\),而 \(h_S\) 的转移是容易的,就设 \(S\) 为 \(x\) 中最小的元素,然后枚举 \(x\) 所在的连通块 \(x\in T\subsetneq S\)​,贡献为 \(g_Tf_{S\backslash T}\),简单枚举个子集即可,时间复杂度 \(3^n\)。

注:现场 80pts 的代码,部分变量名称可能与上面不符,同时为了卡常我在求解 \(h_S\) 那一步强制要求 \(S\) 中最大的元素(instead of 最小的元素)必须属于 \(T\),这样一旦不符合即可 break 掉:

const int MAXM=136;
const int MAXP=1<<17;
const int MOD=998244353;
int n,m,pw2[MAXM+5];
int f[MAXP+5],g[MAXP+5],tot[MAXP+5],ed[MAXP+5];
int high(int x){return 31-__builtin_clz(x);}
int main(){
// freopen("youmu.in","r",stdin);freopen("youmu.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=(pw2[0]=1);i<=m;i++) pw2[i]=pw2[i-1]*2%MOD;
for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),ed[(1<<u-1)|(1<<v-1)]++;
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++) if(j>>i&1) ed[j]+=ed[j^(1<<i)];
for(int i=1;i<(1<<n);i++){
for(int j=i;j;j=(j-1)&i){
if(high(j)!=high(i)) break;
tot[i]=(tot[i]+pw2[ed[i]-ed[j]-ed[i^j]])%MOD;
}
} f[0]=1;int ans=0;
for(int i=1;i<(1<<n);i++){
int lb=high(i&(-i));
for(int j=i;j;j=(j-1)&i){
if(j>>lb&1) g[i]=(g[i]+2ll*f[j]*tot[i^j])%MOD;
} f[i]=(tot[i]-g[i]+MOD)%MOD;ans=(ans+f[i])%MOD;
// printf("%d %d %d\n",i,f[i],g[i]);
} printf("%d\n",ans);
return 0;
}

接下来考虑优化,首先是第一步求解 \(f_S\) 的优化,注意到在第一步中,\(B\) 与 \(W\) 之间的边的个数可以用 \(ed_{B\cup W}-ed_B-ed_W\) 来表示,其中 \(ed_S\) 表示 \(S\)​ 中边的个数,那么 \(f_S=\sum\limits_{B\cup W=S,B\cap W=\varnothing}2^{ed_S-ed_B-ed_W}=2^{ed_S}\sum\limits_{B\cup W=S,B\cap W=\varnothing}\dfrac{1}{2^{ed_B}2^{ed_W}}\),容易发现这东西是一个子集卷积的形式,可以 \(2^nn^2\) 做掉。

然后是第二步的优化,这东西现场看来无法优化,所以就没写正解。现在学了子集卷积之后看就很 easy 了,我们记集合幂级数 \(A(x)\) 中 \(x^S\) 前的系数表示以 \(S\) 为点集的连通二分图染色数量,同理 \(B(x)\) 则表示二分染色图的数量,那么不难发现这题中 \(A(x)\) 与 \(B(x)\)​ 的关系就相当于 P4841 [集训队作业2013]城市规划 中,带标号图的数量的 OGF 带标号连通图的数量的 OGF 的关系一样,因此 \(B(x)=\sum\limits_{n\ge 0}\dfrac{A^n}{n!}\),其中乘法表示子集卷积,也就是说 \(B=\exp A\),因此 \(A=\ln B\),不难发现 \([x^S]B(x)=f_S\),因此求个子集 \(\ln\) 即可求出 \(A\),也就是上文中的 \(g_S\)。

时间复杂度就降到了 \(2^nn^2\)。

代码(后面附有两个比较强的样例,没错你没看错,样例 #1 就是这题的样例,样例 #2 就是这题的样例):

const int MAXN=20;
const int MAXM=190;
const int MAXP=1<<20;
const int MOD=998244353;
const int INV2=499122177;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,m,ed[MAXP+5],ppc[MAXP+5],tmp[MAXN+5],inv[MAXN+5],res=0;
int f[MAXN+5][MAXP+5],g[MAXN+5][MAXP+5],pw2[MAXM+5],ipw2[MAXM+5];
void FWTor(int *a,int len,int type){
for(int i=2;i<=len;i<<=1)
for(int j=0;j<len;j+=i)
for(int k=0;k<(i>>1);k++)
a[(i>>1)+j+k]=(a[(i>>1)+j+k]+1ll*a[j+k]*type)%MOD;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=(inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=(pw2[0]=ipw2[0]=1);i<=m;i++) pw2[i]=(pw2[i-1]<<1)%MOD,ipw2[i]=1ll*ipw2[i-1]*INV2%MOD;
for(int i=1;i<(1<<n);i++) ppc[i]=ppc[i&(i-1)]+1;
for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),ed[(1<<u-1)|(1<<v-1)]++;
for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++) if(j>>i&1) ed[j]+=ed[j^(1<<i)];
for(int i=0;i<(1<<n);i++) f[ppc[i]][i]=ipw2[ed[i]];
for(int i=0;i<=n;i++) FWTor(f[i],1<<n,1);
for(int i=0;i<=n;i++) for(int j=0;j<(1<<n);j++) for(int l=0;l<=i;l++)
g[i][j]=(g[i][j]+1ll*f[l][j]*f[i-l][j])%MOD;
for(int i=0;i<=n;i++) FWTor(g[i],1<<n,MOD-1);
for(int i=0;i<(1<<n);i++) for(int j=0;j<=n;j++) if(ppc[i]!=j) g[j][i]=0;
for(int i=0;i<(1<<n);i++)
g[ppc[i]][i]=1ll*g[ppc[i]][i]*pw2[ed[i]]%MOD;
// for(int i=0;i<(1<<n);i++) printf("%d %d\n",i,g[ppc[i]][i]);
for(int i=0;i<=n;i++) FWTor(g[i],1<<n,1);
for(int i=0;i<(1<<n);i++){
memset(tmp,0,sizeof(tmp));
for(int j=1;j<=n;j++){
tmp[j]=g[j][i];
for(int k=1;k<j;k++)
tmp[j]=(tmp[j]-1ll*k*tmp[k]%MOD*inv[j]%MOD*g[j-k][i]%MOD+MOD)%MOD;
} for(int j=1;j<=n;j++) g[j][i]=tmp[j];
} for(int i=0;i<=n;i++) FWTor(g[i],1<<n,MOD-1);
// for(int i=0;i<(1<<n);i++) printf("%d %d\n",i,g[ppc[i]][i]);
for(int i=1;i<(1<<n);i++) res=(res+1ll*g[ppc[i]][i]*INV2)%MOD;
printf("%d\n",res);
return 0;
}
/*1
Input:
4 5
1 2
2 4
1 3
1 4
2 3
Output:
26
*/
/*2
Input:
6 13
1 2
1 3
2 3
1 4
4 2
3 4
5 2
3 5
5 4
6 2
6 3
6 4
6 5
Output:
1866
*/

NFLSOJ 1060 - 【2021 六校联合训练 NOI #40】白玉楼今天的饭(子集 ln)的更多相关文章

  1. NFLSOJ 1072 - 【2021 六校联合训练 NOIP #1】异或(FWT+插值)

    题面传送门 一道非常不错的 FWT+插值的题 %%%%%%%%%%%% 还是那句话,反正非六校的看不到题对吧((( 方便起见在下文中设 \(n=2^d\). 首先很明显的一点是这题涉及两个维度:异或和 ...

  2. 题解 nflsoj553 【六校联合训练 省选 #10】飞

    题目链接 我们称"简要题意"给出的三个要求分别为"条件1","条件2","条件3". 条件3长得比较丑,考虑转化一下.把 ...

  3. 题解 nflsoj550 【六校联合训练 省选 #9】序列

    题目链接 以下把值域(题面里的\(lim\))记做\(m\). 考虑求\(k\)的答案.考虑每个位置对答案的贡献,枚举位置\(i\),再枚举\(a[i]\)的值\(x\).设: \[ F(k)=\su ...

  4. 题解 nflsoj489 【六校联合训练 CSP #15】小D与随机

    题目链接 考虑枚举好点的集合.此时要考虑的问题是如何填入\(1\sim n\)这些数使得恰好我们枚举到的这些点是好点,即:求出有多少种合法的填数方案. \(1\)号点一定是好点.那么除\(1\)号点外 ...

  5. HDU 5358(2015多校联合训练赛第六场1006) First One (区间合并+常数优化)

    pid=5358">HDU 5358 题意: 求∑​i=1​n​​∑​j=i​n​​(⌊log​2​​S(i,j)⌋+1)∗(i+j). 思路: S(i,j) < 10^10 & ...

  6. 2017多校联合训练2—HDU6054--Is Derek lying?(思维题)

    Is Derek lying? Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  7. 2017ACM暑期多校联合训练 - Team 7 1010 HDU 6129 Just do it (找规律)

    题目链接 Problem Description There is a nonnegative integer sequence a1...n of length n. HazelFan wants ...

  8. HDU6336-2018ACM暑假多校联合训练4-1005-Problem E. Matrix from Arrays-前缀和

    题意是给了一种矩阵的生成方式 让你求两个左边之间的矩阵里面的数加起来的和(不是求矩阵的值) 没看标程之前硬撸写了160行 用了前缀和以后代码量缩短到原来的1/3 根据规律可以推导出这个矩阵是在不断重复 ...

  9. 2016多校联合训练4 F - Substring 后缀数组

    Description ?? is practicing his program skill, and now he is given a string, he has to calculate th ...

随机推荐

  1. 【数据结构】<栈的应用>回文判断

    通过栈与队列相关内容的学习,我们知道,栈是"先进后出"的线性表,而队列是"先进先出"的线性表.可以通过构造栈与队列来实现在这一算法.将要判断的字符序列依次压栈和 ...

  2. 这样调优之后,单机也能扛下100W连接

    1 模拟单机连接瓶颈 我们知道,通常启动一个服务端会绑定一个端口,例如8000端口,当然客户端连接端口是有限制的,除去最大端口65535和默认的1024端口及以下的端口,就只剩下1 024~65 53 ...

  3. UltraSoft - Alpha - Scrum Meeting 2

    Date: Apr 09th, 2020. 会议内容为完成初步的任务分工. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM.后端 继续Django tuto ...

  4. 快速了解XML

    1. XML 定义 可扩展标记语言,标准通用标记语言的子集,简称XML.是一种用于标记电子文件使其具有结构性的标记语言. 2. XML 展示 如下是一个xml的标记展示,XML 是不作为的XML 被设 ...

  5. 【做题记录】[NOIP2011 提高组] 观光公交

    P1315 [NOIP2011 提高组] 观光公交 我们想在 \(k\) 次加速每一次都取当前最优的方案加速. 考虑怎样计算对于每一条边如果在当前情况下使用加速器能够使答案减少的大小. 如果当前到达某 ...

  6. 算法:拉丁方阵(Latin Square)

    拉丁方阵(英语:Latin square)是一种 n × n 的方阵,在这种 n × n 的方阵里,恰有 n 种不同的元素,每一种不同的元素在同一行或同一列里只出现一次.以下是两个拉丁方阵举例: 拉丁 ...

  7. Envoy实现.NET架构的网关(三)代理GRPC

    什么是GRPC gRPC是一种与语言无关的高性能远程过程调用 (RPC) 框架.gRPC 的主要好处是: 现代.高性能.轻量级的 RPC 框架. 契约优先的 API 开发,默认使用协议缓冲区,与语言无 ...

  8. vim 删除 替换

    1,删除包含pattern的某一行 :g/pattern/d 或 :%g/pattern/d 2,删除不包含pattern的某一行 :v/pattern/d 或 :g!/pattern/d 3,替换 ...

  9. hdu 3199 Hamming Problem(构造?枚举?)

    题意: For each three prime numbers p1, p2 and p3, let's define Hamming sequence Hi(p1, p2, p3), i=1, . ...

  10. vue mvc与mvvm

    一.什么是MVVM? MVVM是Model-View-ViewModel的缩写.MVVM是一种设计思想.Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑:View 代表UI ...