loj3053
引言
那就是希望。
即便需要取模,也是光明。
它还是来了。
这题我尝试写过一次,寄了。然后开摆了。
现在决定重新补一补这题。
敬请收看:myee 调长剖调到 CSP 还没有调出来的惨状!
欢迎来看我什么时候补掉。当然也可能鸽了。
思路
注意到是连通块信息计数。
注意到这个贡献不方便计算。
考虑点减边容斥。
我们计算对每个点,其向儿子子树距离不超过 \(L,L-1\) 的连通块方案数,与向父亲子树距离不超过 \(L\) 的连通块方案数,即可计算出答案。
我们设其为 \(h_{p,x},g_{p,x}\)。
则,我们有
然后容易发现每个点、边的贡献均可以用 \(h_{p,L},h_{p,L-1},g_{p,L}\) 表出。
问题是怎么快速计算。
计算 \(h\)
注意到 \(x\) 是一个和深度相关的量,于是考虑长链剖分。
现在要支持全局加、前缀单点乘、后缀乘操作。
(注意,\(h\) 是无限长的,因此长剖信息会出现一段后缀乘)
考虑直接维护这种玩意儿。
一种方法是线段树,但显然常数忒大,跑不过去。
考虑维护全局乘法标记,然后前缀求逆暴力乘。
容易发现可能会除 \(0\)……然后就 GG 了……
考虑怎么特判后缀乘 \(0\) 的操作。
考虑打个标记,维护当前从哪一项开始全是 \(0\)。
但是这个标记在全局加 \(1\) 后就会失效,考虑怎么办。
把标记改定义为:\(h_{p,\ge a}=C\),其中 \(a,C\) 被计入 \(tag\)。
这样就可以兼容全局加了。
诶,我上次是不是这里就写成从某点开始均为 \(0\) 的标记了。
计算 \(g\)
首先,容易发现,我们对于每一个点,只用维护
即可得到所有答案中的 \(g_{p,L}\)。
问题是怎么维护。
考虑先对根节点暴力计算 \(g\)。
这个很简单。全部都是 \(1\)。
然后下传信息。
先传轻儿子,再传重儿子。
对轻儿子,直接重新建一段信息链。
对重儿子,继承当前点信息再全局加 \(1\)。
问题是,如何统计上 \(\prod_{f_a=f,a\neq p}(h_{a,x-2}+1)\) 的贡献?
一种想法是用除法,\(\prod_{f_a=f,a\neq p}(h_{a,x-2}+1)=h_{f,x-1}/h_{p,x-2}\)。
非常抱歉,然而这样会出现 \(0/0\),是未定式。
我们不能指望通过统计区间有几个 \(0\) 使信息可减,因为长剖天然性质。
因此只能借助于维护前后缀积了。
对前缀积,可以把前面计算 \(h\) 的过程可持久化,计算完某轻儿子的 \(g\) 之后把其对应 \(h\) 的贡献暴力加入 \(g\)。
那后缀积呢?
注意到我们计算 \(h_f\) 时,如果枚举儿子顺序恰好为当前计算 \(g_f\) 时枚举顺序的逆序,则其的每一个版本恰好就对应着一个 \(h_a+1\) 的后缀积。
对此,直接暴力更新儿子节点信息即可。
然后由于版本访问时间的特性,可以使用可回退化数据结构代替可持久化。
Code 实时记录
\(11\!:\!53\):准备开始写。
\(14\!:\!08\):写了个可回退化修改元素的 DS。
template<typename T> // 可回退化 DS
struct Roll{
std::vector<T*>At;std::vector<T>W;uint tp;
Roll():At(),W(),tp(0){}
voi clear(){At.clear(),W.clear(),tp=0;}
voi chg(T&p,T v){
if(At.size()>tp)At.resize(tp),W.resize(tp);
At.push_back(&p),W.push_back(p),p=v,tp++;
}
voi step_on(){if(tp<At.size())std::swap(*At[tp],W[tp]),tp++;}
voi step_back(){if(tp)--tp,std::swap(*At[tp],W[tp]);}
voi go_time(uint t){
_min(t,(uint)At.size());
while(tp<t)step_on();
while(tp>t)step_back();
}
};
\(14\!:\!53\):写了长剖的第一个 DS。
struct vec1{
modint*P,mul,add,C;uint from,siz;Roll<uint>Ru;Roll<modint>Rv;
vec1():P(NULL),mul(1),add(),C(),from(0),siz(0),Ru(),Rv(){}
voi build(uint len){P=NewMemory(len);}
voi chg(uint&a,uint b){Ru.chg(a,b);}
voi chg(modint&a,modint b){Rv.chg(a,b);}
modint val(uint p){return from+p>=siz?C:P[siz-p-1]*mul+add;}
voi push(){chg(P[siz],-add/mul),chg(C,C+1),chg(add,add+1),chg(siz,siz+1);}
voi merge(vec1&p){
while(from>siz-p.siz)chg(from,from-1),chg(P[from],(C-add)/mul);
modint a=p.val(1e9);
if(a.empty()){
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],(val(i)*p.val(i)-add)/mul);
chg(C,0),chg(from,siz-p.siz);
}
else{
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],val(i)*p.val(i));
chg(mul,mul*a),chg(add,add*a),chg(C,C*a);
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],(P[siz-i-1]-add)/mul);
}
}
};
\(16\!:\!08\):调出了长剖的第一部分,然后加了点优化。
\(17\!:\!42\):(不加懒惰标记地)暴力实现了长剖的第二部分。
\(18\!:\!11\):发现可回退化内存池写挂了。(上面的代码已经修过了)改掉后 Luogu \(80\rm pts\),loj \(76\rm pts\)。
\(18\!:\!50\):不想在 \(h\) 的计算中写 \(g\) 的那个优化了,直接把第二部分中可能除零的懒标记用暴力修改代替,直接过了……甚至离线求逆元我都没有写……有没有神仙教教这么做的复杂度对不对啊?能不能卡啊?
唔 qoj 测了一下 T 掉了最后一个点……Luogu 上不开 O2 也过不去……似乎卡的是常数不是复杂性?所以这个东西到底卡不卡的掉啊?发现内存池写的不好,去改改。
\(19\!:\!05\):qoj 上通过了。
挂核心代码。
const ullt Mod=998244353;
typedef ConstMod::mod_ullt<Mod>modint;
typedef std::vector<modint>modvec;
template<typename T> // 可回退化 DS
struct Roll{
std::vector<T*>At;std::vector<T>W;uint tp;
Roll():At(),W(),tp(0){}
voi chg(T&p,T v){At.push_back(&p),W.push_back(p),p=v,tp++;}
voi revoke(uint t){while(tp>t)--tp,*At[tp]=W[tp];}
};
modint _Base[20000005],*End=_Base+20000000;
inline modint*NewMemory(uint n){return End-=n;}
struct vec1{
modint*P,mul,add,inv,C;uint from,siz;Roll<uint>Ru;Roll<modint>Rv;
vec1():P(NULL),mul(1),add(),inv(1),C(),from(0),siz(0),Ru(),Rv(){}
voi build(uint len){P=NewMemory(len);}
voi chg(uint&a,uint b){Ru.chg(a,b);}
voi chg(modint&a,modint b){Rv.chg(a,b);}
modint val(uint p){return from+p>=siz?C:P[siz-p-1]*mul+add;}
voi push(){chg(P[siz],-add*inv),chg(C,C+1),chg(add,add+1),chg(siz,siz+1);}
voi merge(vec1&p){
while(from>siz-p.siz)chg(from,from-1),chg(P[from],(C-add)*inv);
modint a=p.val(1e9);
if(a.empty()){
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],(val(i)*p.val(i)-add)*inv);
chg(C,0),chg(from,siz-p.siz);
}
else{
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],val(i)*p.val(i));
chg(mul,mul*a),chg(add,add*a),chg(C,C*a),chg(inv,inv/a);
for(uint i=0;i<p.siz;i++)chg(P[siz-i-1],(P[siz-i-1]-add)*inv);
}
}
};
std::vector<uint>Way[1000005],Son[1000005];
uint Dep[1000005],MaxDep[1000005],Heavy[1000005],Fath[1000005],x;
voi dfs0(uint p,uint f){
Heavy[p]=-1,MaxDep[p]=Dep[p],Fath[p]=f;
for(auto s:Way[p])if(s!=f)Dep[s]=Dep[p]+1,dfs0(s,p),Son[p].push_back(s);
std::sort(Son[p].begin(),Son[p].end(),[&](uint a,uint b){return MaxDep[a]>MaxDep[b];});
if(Son[p].size())MaxDep[p]=MaxDep[Heavy[p]=Son[p][0]];
}
vec1 H[1000005];uint At[1000005];
modint GetVal[3][1000005];
uint Time[2][1000005];
voi dfs1(uint p,uint r){
if(~Heavy[p])dfs1(Heavy[p],r),At[p]=At[Heavy[p]];else H[At[p]=p].build(Dep[p]-Dep[r]+2);
vec1&h=H[At[p]];h.push();
for(auto s:Son[p])if(s!=Heavy[p])
dfs1(s,s),Time[0][s]=h.Ru.tp,Time[1][s]=h.Rv.tp,H[At[s]].push(),h.merge(H[At[s]]);
GetVal[0][p]=h.val(x);if(x)GetVal[1][p]=h.val(x-1);
}
struct vec2{
modint*P;modint add,mul,inv;uint siz;
vec2():P(NULL),add(),mul(1),inv(1),siz(){}
voi build(uint len){P=NewMemory(siz=len);}
modint val(uint p){return P[siz-p-1]*mul+add;}
voi pop(){siz--;}
};
vec2 G[1000005];uint At2[1000005];
voi dfs2(uint p){
vec1&h=H[At[p]];vec2&g=G[At2[p]];
if(!~Fath[p])g.build(MaxDep[p]+1),g.add=1;
GetVal[2][p]=g.val(0),g.pop();
if(!~Heavy[p])return;
std::reverse(Son[p].begin(),Son[p].end());
for(auto s:Son[p])if(s!=Heavy[p]){
h.Ru.revoke(Time[0][s]),h.Rv.revoke(Time[1][s]);
vec2&gs=G[At2[s]=s];
gs.build(MaxDep[s]-Dep[s]+1),gs.add=1;
for(uint i=0;i<gs.siz&&i<x;i++)
gs.P[gs.siz-i-1]=g.val(i)*h.val(x-i-1);
vec1&hs=H[At[s]];
if(hs.siz>=std::min(g.siz,x)){
for(uint i=0;i<g.siz&&i<x;i++)g.P[g.siz-i-1]=(hs.val(x-i-1)*g.val(i)-g.add)*g.inv;
}else{
if(hs.val(1e9).empty()){
for(uint i=0;i<g.siz&&i<x;i++)g.P[g.siz-i-1]=(hs.val(x-i-1)*g.val(i)-g.add)*g.inv;
}
else{
for(uint i=std::min(x,g.siz)-hs.siz;i<std::min(x,g.siz);i++)
g.P[g.siz-i-1]=hs.val(x-i-1)*g.val(i);
g.mul*=hs.val(1e9),g.add*=hs.val(1e9),g.inv/=hs.val(1e9);
for(uint i=std::min(x,g.siz)-hs.siz;i<std::min(x,g.siz);i++)
g.P[g.siz-i-1]=(g.P[g.siz-i-1]-g.add)*g.inv;
}
}
dfs2(s);
}
if(g.siz>x)g.P[g.siz-x-1]=-g.add*g.inv;
G[At2[Heavy[p]]=At2[p]].add++,dfs2(Heavy[p]);
}
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
#endif
uint n,k;scanf("%u%u%u",&n,&x,&k);
for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u);
dfs0(0,-1),dfs1(0,0),dfs2(0);modint ans;
for(uint i=0;i<n;i++)ans+=(GetVal[0][i]*GetVal[2][i])^k;
for(uint i=1;i<n;i++)ans-=(GetVal[1][i]*(GetVal[2][i]-1))^k;
ans.println();
return 0;
}
本文来自博客园,作者:myee,转载请注明原文链接:https://www.cnblogs.com/myee/p/loj3053.html