专栏文章
题解:CF2063F2 Counting Is Not Fun (Hard Version)
CF2063F2题解参与者 3已保存评论 5
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 5 条
- 当前快照
- 1 份
- 快照标识符
- @miqf0hyl
- 此快照首次捕获于
- 2025/12/04 03:43 3 个月前
- 此快照最后确认于
- 2025/12/04 03:43 3 个月前
提供一个疑似是本题最简单的做法,并且我们爆标了。practical 的复杂度是 ,使用线性树上并查集我们可以做到线性。
感谢 StayAlone提供的细节帮助,感谢 AfterFullStop指出本做法可以做到线性。
回顾 F1 的做法:
我们对已知的匹配括号建立括号串分治树。具体来说,树上每个节点都是一对匹配括号 ,我们按照它们的包含和不交关系建立成树。
有了树结构,答案是容易表示的。我们维护每个节点下没有被儿子匹配管辖的空位的长度 ,即 。它产生的贡献就是这一长度的合法括号串的数量,也就是一个卡特兰数 。
答案把所有节点的贡献乘起来就好。
延续 F1 的做法。我们尝试维护分治树,考虑每次加入一个节点产生的影响。因为加入节点需要操作子树,这不太方便,我们考虑时光倒流。
时光倒流之后,每次我们只需删除一个节点,把它的儿子挂到它的父亲上。贡献上会产生的影响只有删去它的 的贡献,再改变它父亲的 的贡献,结构上也只会影响此处的父亲关系。
我们直接用逆元撤销掉原来的贡献,再把新的贡献加上去,同时维护 。
考虑剩下的问题是我们还需要也仅需要维护分治树上的父亲关系,需要支持删除一个节点。类似可并堆的处理办法,考虑我们没必要真实删除这个节点,把它重定向到它的父亲即可。用一个并查集就可以简单维护好。
实现上,因为分治树上所有节点的端点都互不相同,所以我们可以从端点映射节点规避
map;卡特兰数及其逆元可以用线性阶乘逆元、线性逆元 得到。于是时间复杂度瓶颈是并查集。树上并查集直接写可以做到 ,使用线性树上并查集就可以把整个东西打成线性。
下面是一个懒得写按秩合并所以带 的代码:
CPPint tc;
int n;
ll fac[N],ifac[N],inv[N];
ll qpow(ll b,int p){
if(!p) return 1;
ll d=qpow(b,p>>1);
if(p&1) return d*d%mod*b%mod;
else return d*d%mod;
}
void init(int n=1.2e6){
fac[0]=1;
fr1(i,1,n) fac[i]=fac[i-1]*i%mod;
ifac[n]=qpow(fac[n],mod-2);
fr2(i,n-1,0) ifac[i]=ifac[i+1]*(i+1)%mod;
fr1(i,1,n) inv[i]=ifac[i]*fac[i-1]%mod;
}
ll Ca(int n){
return fac[n]*ifac[n>>1]%mod*ifac[n>>1]%mod*inv[(n>>1)+1]%mod;
}
ll iCa(int n){
return ifac[n]*fac[n>>1]%mod*fac[n>>1]%mod*((n>>1)+1)%mod;
}
int mat[N];
vector <int> op;
vector <int> res;
int fa[N],dirf[N];
int len[N];
int node[N];
int tot;
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void divide(int f,int l,int r){
tot++;
node[l]=tot;
len[tot]=0,dirf[tot]=f,fa[tot]=tot;
int cnt=0;
fr1(i,l+1,r-1){
divide(node[l],i,mat[i]);
i=mat[i];
}
}
void solve(){
tot=0;
op.clear(),res.clear();
cin>>n;
fr1(i,1,(n<<1)) mat[i]=0;
fr1(i,1,n){
int u,v;
cin>>u>>v;
mat[u]=v;
op.pb(u);
}
divide(0,0,(n<<1)+1);
ll ans=1;
fr2(i,n-1,0){
res.pb(ans);
int id=op[i];
int u=node[id];
int f=find(dirf[u]);
(ans*=iCa(len[u]))%=mod;
(ans*=iCa(len[f]))%=mod;
len[f]+=len[u]+2;
(ans*=Ca(len[f]))%=mod;
fa[u]=f;
}
res.pb(ans);
while(!res.empty()){
cout<<res.back()<<" ";
res.pop_back();
}
cout<<'\n';
}
相关推荐
评论
共 5 条评论,欢迎与作者交流。
正在加载评论...