description
小w这学期选了门图论课,他在学习点着色的知识。他现在得到了一张无向图,并希望在这张图上使用最多n种颜色给每个节点染色,使得任意一条边关联的两个节点颜色不同。
小w获得一张n个节点m条边的基图,并得到了一份神秘代码。他会根据这份代码的内容构建完整的无向图。
while(1){
int modify_tag=0;
for(int x=1;x<=n;x++)
for(int y=x+1;y<=n;y++)
for(int z=y+1;z<=n;z++)
if(edge(x,y)∈G && edge(x,z)∈G){
add edge(y,z) to G
modify_tag=1;
}
if(modify_tag==0) break;
}
即对于图上的任意三元组x<y<z,若(x,y),(x,z)在图中则在图上加上一条(y,z)的边,直至无法加边为止。
小w想要知道使用n种颜色给这张基图生成的完整无向图的染色方案数。小w太菜了,他无力解决这个难题,于是只好把它交给了你。
analysis
-
首先有一个结论,ans=∏n−g[i]ans=\prod n-g[i]ans=∏n−g[i],g[i]g[i]g[i]表示与iii相连、编号比iii大的节点数量
-
如果从大到小染色染到第iii位,g[i]g[i]g[i]已经染过色了且iii点和这g[i]g[i]g[i]个点构成完全图
-
那么这g[i]g[i]g[i]个点颜色都不相同,iii位染色的方案数就是n−g[i]n-g[i]n−g[i],以此类推
-
暴力做法是O(n2)O(n^2)O(n2)把这个图建出来,搞出每一个g[i]g[i]g[i],全部乘起来
-
其实可以考虑维护nnn棵线段树,存储第iii位向后的连边情况
-
编号从小到大合并,假设合并到第kkk位,区间查询比kkk大有多少已经连边的点,乘进答案
-
然后再找出比kkk大且最小的存在的编号,把kkk的线段树合并到该编号的线段树就可以了
-
也可以用setsetset维护连点的集合,合并两集合就启发式合并,但这个我还不是很懂
code
线段树合并
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 100005
#define MAX MAXN*50
#define ha 998244353
#define ll long long
#define reg register ll
#

最低0.47元/天 解锁文章
168万+

被折叠的 条评论
为什么被折叠?



