【学习笔记】CF1292F Nora‘s Toy Boxes

没啥好说的,接着干吧

这种把性质隐藏得很深的题目非常有趣。

以我现在的水平,估计还是无法独立做出来

首先我们对于 ( i , j ) (i,j) (i,j)如果满足 a i ∣ a j a_i|a_j aiaj,那么 i i i j j j连一条边。

当然,这道题并不是随便给出一个图那么简单。我们可以推出几个简单的性质:

1.1 1.1 1.1 如果 i → j , j → k i\to j,j\to k ijjk,那么 i → k i\to k ik (传递闭包性质)
1.2 1.2 1.2 这是一个 D A G DAG DAG

这个性质看起来非常亲切啊。这样我们每次选择的 i i i一定是入度为 0 0 0的点,并且这个点一定不会被删除。更神奇的是,这样的点不会超过 m 4 \frac{m}{4} 4m个。这个用抽屉原理不难理解。

那么我们考虑倒着做,一个点能够被加入当且仅当存在一个度数 > 0 >0 >0并且有连边的关键点,这里的 d p dp dp非常具有人类智慧,我们需要维护一个标记。假设关键点的集合为 S S S,最开始所有关键点都没有标记,假设加入的点没有重复,如果这个点能到达的关键点当中存在一个点 p p p被标记,那么这个点是被删除的,否则这个点没有被删除;然后把这个点能到达的所有关键点打上标记。

那么我们考虑,如果 S S S发生了变化,说明这个点之前没有被加入过,否则,这个点显然被删除了,注意到 S S S是一个并集,那么我们只需要记录之前被删除的点数目即可。这里有一个小小的推导过程(我在这里还是呈现出来了):记数字 i i i对应的关键点集合为 S i S_i Si,那么没有被删除的点的 S i S_i Si显然是不交的,并且被删除的节点 j j j对应的 S j S_j Sj不可能使这些 S i S_i Si重新相交(这可以画图通过贪心证明,因为要使得删除的点的数目最多)。因此,假设有 s z sz sz个点加入后不会改变 S S S的大小,并且之前已经删除了 i i i个点,那么能加入的点的数目为 s z − i − 1 sz-i-1 szi1(其中一个点没有被删除)。这样就做完了。当然因为我们是对于每一个弱连通块计数所以不难证明这个连通块当中恰好只有一个点没有被删除。

最后把所有连通块的答案合并起来即可。

复杂度 O ( 2 m 4 n 2 ) O(2^\frac{m}{4}n^2) O(24mn2)

#include<bits/stdc++.h> #define ll long long #define pb push_back #define db double using namespace std; const int mod=1e9+7; int n,m,a[65],vis[65],fa[65],p[65],binom[65][65],g[65],pos[65],sz[1<<15],m2; ll f[1<<15][65],res=1; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void unionset(int x,int y){ int u=find(x),v=find(y); if(u!=v)fa[u]=v; } void add(ll &x,ll y){ x=(x+y)%mod; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n; for(int i=0;i<=60;i++)binom[i][0]=1; for(int i=1;i<=60;i++){ for(int j=1;j<=i;j++){ binom[i][j]=(binom[i-1][j]+binom[i-1][j-1])%mod; } } for(int i=1;i<=n;i++){ int x;cin>>x,a[x]=1; } for(int i=1;i<=60;i++)fa[i]=i; for(int i=1;i<=60;i++){ for(int j=i+1;j<=60;j++){ if(a[i]&&a[j]&&j%i==0){ vis[j]=1; unionset(i,j); } } } int cnt=0; for(int i=1;i<=60;i++){ if(a[i]&&fa[i]==i){ m=m2=0; for(int j=1;j<=60;j++){ if(a[j]&&find(j)==i){ if(vis[j]){ pos[m2++]=j; } else if(j<=30){ p[m++]=j; } } } assert(m<=15); if(!m2)continue; memset(f,0,sizeof f); for(int j=0;j<m2;j++){ g[j]=0; for(int k=0;k<m;k++){ if(pos[j]%p[k]==0){ g[j]|=1<<k; } } assert(g[j]); f[g[j]][0]++; } for(int j=0;j<1<<m;j++){ sz[j]=0; for(int k=0;k<m2;k++){ if((g[k]&j)==g[k]){ sz[j]++; } } } for(int k=0;k<=m2;k++){ for(int j=0;j<1<<m;j++){ if(f[j][k]){ //fixed assert(k<sz[j]); add(f[j][k+1],f[j][k]*(sz[j]-k-1)); for(int l=0;l<m2;l++){ //fixed if((j|g[l])!=j&&(j&g[l])!=0){ add(f[j|g[l]][k+1],f[j][k]); } } } } } ll tot=0; for(int j=0;j<1<<m;j++){ add(tot,f[j][m2-1]); } res=res*tot%mod*binom[cnt+m2-1][m2-1]%mod; cnt+=m2-1; } } cout<<res; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529999.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-04-06 【题解】[POI2008] ROB-Robinson
点击右上角即可分享
微信分享提示