P8280 「MCOI-08」Photoelectric Effect 题解

もとの題

题意

给定一棵 nn 个节点的树,让你在这 nn 个节点上染色,有 kk 种颜色可选,aia_i 为第 ii 个节点的颜色。

给出一个合并函数 \otimes,使得对于所有的、没有祖先关系的两个节点 u,vu,v,有 auav=alca(u,v)a_u \otimes a_v=a_{\operatorname{lca}(u,v)},求出能够满足条件的染色方案数,对 109+710^9+7 取模。

1n,n1051\leq n,\sum n\leq10^52k52\leq k\leq51fi<i1\leq f_i<i

思路

我们可以发现当 u,vu,v 没有祖先关系时,他们一定会在以某一个节点 xx 的某两个儿子为根的子树上(这不是废话么)。

那么就可以 分别xx 的两个儿子的子树中选一个点,这两个点叫 u,vu,v,使得 lca(u,v)=x\operatorname{lca}(u,v)=x

有了 u,v,xu,v,x,且 lca(u,v)=x\operatorname{lca}(u,v)=x,我们需要确保的是 auav=axa_u \otimes a_v=a_x

那子树中所有的 u,vu,v 是都要满足这个条件的。

我们可以选择枚举 axa_x,遍历所有的 uuvv,然后 check,但会 T。


观察发现,满足的条件 auav=axa_u \otimes a_v=a_x 中“决定性的”变量只有 au,av,axa_u,a_v,a_x

我们又可以发现颜色种类 kk 的值非常小,只有 55,也就是上面的子树中重复的 aua_uava_v 很多,不妨考虑状态压缩。

然后我们就可以用状态压缩表示子树中节点出现的颜色 集合 了。当一个颜色 ok 时,所有有该颜色的 uu 都会 ok。


假设我们已经知道了 xx 儿子的子树中出现的颜色,我们是可以通过他们来推测 xx 的颜色的。

或者说,我也可以枚举 xx 的颜色,然后对照着表格进行检查。

具体而言,在 \otimes 矩阵中,我们可以判断 xx 儿子子树对于颜色的选择代入到矩阵中是否出现唯一的颜色,如果是唯一的,xx 可以用该颜色,如果不是,那无论 xx 取什么颜色,都存在 (u,v)(u,v) 使得颜色并不是 colxcol_x

以下是一个例子:

xx 的左子树中,出现了 3,4,53,4,5 的颜色,右子树出现了 1,2,31,2,3

可以看到,在并函数结果,仅包含 3,4,53,4,5 行,1,2,31,2,3 列的子矩阵中,有 1,21,2 两种颜色。

这说明了什么?如果左边取 55,右边取 33xx 的颜色就是 22,其他情况,xx 的颜色是 11

这也就说明了因为 xx 不能同时拥有两种颜色,所以上面无解。

又因为我们假设已经知道 xx 子树出现的集合,所以判断有无解是可以预处理的。

预处理代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//这里使用颜色从 0 开始
//颜色从 0 开始只会在代码中体现,正文从 1 开始
inline void S_to_list(int s,int *_list){
_list[0]=0;
for(int i=0;i<k;++i){
if(s&1) _list[++_list[0]]=i;
s>>=1;
}
}//将颜色集合 S 转为颜色列表

ll color[maxm][maxm];
//若集合 1 与集合 2 出现有解,color [集合 1][集合2] 表示其 lca(即上文 x)应取的颜色,若无解,为-1
inline void check_col(int s1,int s2,int *s1choose,int *s2choose){
int lastcol=-1;
//检查子矩阵中颜色是否单一
for(int it=1;it<=s1choose[0];++it){
for(int jt=1;jt<=s2choose[0];++jt){
int i=s1choose[it],j=s2choose[jt];
if(lastcol==-1) lastcol=_xor[i][j];
else if(_xor[i][j]!=lastcol){
//如果不单一,放弃吧,保留-1
return;
}
}
}
color[s1][s2]=lastcol;//单一即 x 应取的颜色
}
inline void calc_col(){
int s1choose[maxk]={},s2choose[maxk]={};
for(int s1=1;s1<(1<<k);++s1){
for(int s2=1;s2<(1<<k);++s2){//枚举集合 1,2
S_to_list(s1,s1choose);
S_to_list(s2,s2choose);
check_col(s1,s2,s1choose,s2choose);
}
}
}
void init(){
memset(color,-1,sizeof color);//预设-1
}

好了,假设破灭了,我们并不知道子树出现的颜色。但是可以发现,对于叶节点而言,出现的颜色集合是可以枚举的——无非就那五个。

你想到了什么?有初始状态,能转移,无后效性
dp!

行,设 dpx,Sdp_{x,S} 表示当前节点为 xx包括 xx 点及其子树内所有点 的颜色后,出现的颜色集合为 SS 的方案数,SS 用状态压缩表示。

首先,对于每个叶子节点 xxdpx,S=1,S={ii[1,k]}dp_{x,S}=1,S=\{i|i\in[1,k]\},因为叶子节点能且只能选一种颜色,所以对于其他的 SSdpx,S=0dp_{x,S}=0

然后,就到非叶子节点 xx 了,可以先讨论 xx 的颜色,再 check。

不妨设对于 xx,当前讨论到了第 ii 个儿子为 yiy_isumx,i,Ssum_{x,i,S} 表示前 ii 个儿子出现的颜色集合为 SS 的方案数(不含节点 xx)。

显然的,我们可以将 sumx,1,S=dpy1,Ssum_{x,1,S}=dp_{y_1,S}

然后,对于第 ii 个儿子,有:

lastSthisSsumx,i1,lastS×dpyi,thisS×[colorlastS,thisS=ax]sumx,i,lastSthisS\sum\limits_{lastS} \sum\limits_{thisS} sum_{x,i-1,lastS} \times dp_{yi,thisS} \times [color_{lastS,thisS}=a_x] \Rightarrow sum_{x,i,lastS \,\cup \,thisS}

对变量的解释:

  • colorcolor 是预处理的 给定两个颜色集合,返回这两个集合颜色的并的唯一值。不唯一设为 0。

  • axa_x 为枚举的 xx 点颜色。

  • thisSthisS当前 儿子子树下出现的颜色集合,lastSlastS之前 儿子子树下出现的颜色集合。

这样做为什么是对的呢?假设只有 22 个儿子,我需要从左儿子中选 lastSlastS 的颜色,再从右儿子中选 thisSthisS 的颜色。如果 colorlastS,thisS=axcolor_{lastS,thisS}=a_x,即有解,并且解为 xx 的颜色,不矛盾,那就先预存在 sumx,i,lastSthisSsum_{x,i,lastS \,\cup \,thisS} 中,出现的颜色就是 lastSlastSthisSthisS 的交集。

如果有 33 个及更多儿子,首先少一个儿子,并且 xx 的颜色是 axa_x 的情况数是确定的。那么,我如果要加入一个新的颜色集合 thisSthisS,就必然要跟前面子树出现过的点,也就是前面子树出现过的颜色一一判断,ok 了才能加。碰巧啊,前面子树出现过的颜色不就是 lastSlastS 吗?

最后的话,只需要将最后的儿子结果再带回 dpx,Saxdp_{x,S\cup a_x} 就可以了,即 sumx,soncnt,Sdpx,Saxsum_{x,soncnt,S} \Rightarrow dp_{x,S \cup a_x},其中 soncntsoncntxx 节点的儿子数目。

代码就不贴了,写的太难看了(搬博客时候的评价)


P8280 「MCOI-08」Photoelectric Effect 题解
https://formu1-github.github.io/Hexo-blog/2024/11/14/P8280-「MCOI-08」Photoelectric-Effect-题解/
作者
Formu1
发布于
2024年11月14日
许可协议