题解:洛谷 P3109([USACO14OPEN] Code Breaking G)
1. Description
给定一棵 \(n\) 个节点的树,要求在每个点中填上 \(0\sim 9\) 的数。
有 \(m\) 个限制,每一个限制形如 \(u\ \bar{ABCDE}\),表示从 \(u\) 和 \(u\) 的 \(1\sim 4\) 级祖先组成的数字不等于 \(\bar{ABCDE}\)。
试求出不满足至少一条限制的方案数。
2. Solution
首先不难想到一个暴力容斥的写法,枚举不满足那些限制,容斥系数为 \((-1)^k\),其中 \(k\) 表示不满足的限制数量,时间复杂度为 \(O(n2^m)\)。
然后自然可以想到通过将容斥系数并入 DP 来求解答案,具体的,我们定义 \(f_{u,S}\) 表示如果我们希望 \(u\) 的祖先的状态是 \(S\),这个 \(S\) 是一个数列,如果 \(S_i=-1\),则 \(i\) 级祖先没有限制,则对从 \(u\) 点开始的所有限制对应的数 \(S\),将 \(f_{u,S}\) 设为 \(-1\),同时,将 \(f_{u,S^\prime},S^\prime=\{-1,-1,-1,-1,-1\}\) 设为 \(1\)。
注意:\(S\) 中如果出现 \(-1\),那么必然只会出现一段连续的 \(-1\) 后缀,这是显然的,因为限制都是对于连续的若干个节点做出的。
然后考虑合并 \(u\) 的儿子 \(v\) 和 \(u\) 本身的信息。
这里也需要注意:此时 \(u\) 的状态中,限制是包含 \(u\) 这个节点本身的,而 \(v\) 的状态中,限制则是不包含 \(v\) 本身的。
考虑两个状态 \(f_{u,S}\) 和 \(f_{v,T}\),则他们可以合并的条件是:\(\forall i\in[1,5],S_i=T_i\ or\ S_i=-1\ or\ T_i=1\),合并到他们两个中 \(-1\) 较少的状态,也就是对于祖先限制更多的状态。
最后,我们需要让 \(u\) 的状态中的限制不包含 \(u\) 本身,这一过程是简单的,直接平移数列,在末尾加 \(-1\) 即可,此时如果原来对 \(u\) 是没有限制,那么就需要将方案数乘 \(10\)。
此时我们就可以得到一个 \(O(nm^2)\) 的做法,仍然无法通过。
这里就需要注意力了,我们发现,\(S\) 和 \(T\) 的合并其实是一个匹配前缀的过程,因此考虑使用 trie 合并来进行转移,则最后去除对 \(u\) 的限制的过程也可描述为合并根的所有儿子。
时间复杂度的分析与线段树合并类似,时间复杂度为 \(O(n+5m)\),可过。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=2e4+5,M=3e5+5,mod=1234567;
const int pw[]={1,10,100,1000,10000,100000,100000};
int n,m;
int rt[N];
struct ST{
int a[5];
ST(){
a[0]=a[1]=a[2]=a[3]=a[4]=-1;
}
bool operator <(const ST &T)const{
if(a[0]^T.a[0])return a[0]<T.a[0];
if(a[1]^T.a[1])return a[1]<T.a[1];
if(a[2]^T.a[2])return a[2]<T.a[2];
if(a[3]^T.a[3])return a[3]<T.a[3];
return a[4]<T.a[4];
}
int& operator [](const int &T){
return a[T];
}
ST trans()const{
ST res;
for(int i=0;i<4;i++)
res.a[i]=a[i+1];
res.a[4]=-1;
return res;
}
};
vector<ST>lim[N];
vector<int>e[N];
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int binpow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
b>>=1;
a=mul(a,a);
}
return res;
}
struct Trie{
int num;
int ch[M][10];
int tag[M],val[M];
int New(){
int p=++num;
for(int i=0;i<10;i++)
ch[p][i]=0;
tag[p]=1,val[p]=0;
return p;
}
void Tag(int p,int v){
tag[p]=mul(tag[p],v);
val[p]=mul(val[p],v);
}
void pushdown(int p){
if(tag[p]==1)return ;
for(int i=0;i<10;i++)
if(ch[p][i])Tag(ch[p][i],tag[p]);
tag[p]=1;
}
void insert(int u,ST s){
int p=u;
for(int i=0;i<5;i++){
if(!ch[p][s[i]])ch[p][s[i]]=New();
p=ch[p][s[i]];
}
val[p]=mod-1;
}
int merge1(int u,int v,int sumu,int sumv){//第一种合并,维护的是 DP 转移
if(u==0&&v==0)return 0;
if(u==0){
Tag(v,sumu);
return v;
}
if(v==0){
Tag(u,sumv);
return u;
}
pushdown(u),pushdown(v);
int tmp=sub(mul(add(sumu,val[u]),add(sumv,val[v])),mul(sumu,sumv));
for(int i=0;i<10;i++)
ch[u][i]=merge1(ch[u][i],ch[v][i],add(sumu,val[u]),add(sumv,val[v]));
val[u]=tmp;
return u;
}
int merge2(int u,int v){
if(u==0&&v==0)return 0;
if(u==0)return v;
if(v==0)return u;
pushdown(u),pushdown(v);
for(int i=0;i<10;i++)
ch[u][i]=merge2(ch[u][i],ch[v][i]);
val[u]=add(val[u],val[v]);
return u;
}
}trie;
void dfs(int u){
rt[u]=trie.New();
trie.val[rt[u]]=1;
for(auto tmp:lim[u])
trie.insert(rt[u],tmp);
for(int v:e[u]){
if(v==u)continue;
dfs(v);
rt[u]=trie.merge1(rt[u],rt[v],0,0);
}
int tmprt=0;
for(int i=0;i<10;i++)
tmprt=trie.merge2(tmprt,trie.ch[rt[u]][i]);
if(!tmprt)tmprt=trie.New();
trie.val[tmprt]=add(trie.val[tmprt],mul(trie.val[rt[u]],10));
rt[u]=tmprt;
}
signed main(){
read(n),read(m);
for(int i=2,fa;i<=n;i++){
cin>>fa;
e[fa+1].push_back(i);
}
for(int i=1,u;i<=m;i++){
cin>>u;
ST tmp;
char c[5];
cin>>c;
for(int i=0;i<5;i++){
tmp.a[i]=c[i]-'0';
}
lim[u+1].push_back(tmp);
}
dfs(1);
write(sub(binpow(10,n),trie.val[rt[1]]));
}

浙公网安备 33010602011771号