【学习笔记】[省选联考 2023] 城市建造

我觉得很厉害。因为考场上完全没有想到按子树大小讨论。

首先考虑树的情况。其实也没有那么复杂:我们找到树的重心,那么这个点一定会被删除,否则存在分出来的连通块大小 < n 2 <\frac{n}{2} <2n,又因为重心没有被删掉所以存在连通块大小 > n 2 >\frac{n}{2} >2n,这样连通块大小之差显然就大于 1 1 1了。因此,不妨将重心作为树根。

先考虑 k = 0 k=0 k=0的情况。假设我们已经知道了连通块的大小 t t t,那么我们通过比较子树和 t t t的大小关系就能知道那些点被删除了;应当注意到, t t t应该是子树大小的因数,因此直接暴力跑就做完了。

我们考虑把这个做法搬到圆方树上面去。显然对于一个点双内的点,要么全部删掉,要么删掉的点的数目 ≤ 1 \le 1 1。不妨将问题做一些泛化 :设方点的点权为 0 0 0,原点的点权为 1 1 1,找到树的带权重心,显然在树上的推论仍然成立(这里有一个小小的推导过程:如果是圆点,那么可以看成这个原点向其他相邻的圆点连边,并且满足约束关系,显然这个点还是重心,因此这个点一定会被删掉;如果是方点,那么这个点双一定会被删掉,可以规约到以圆点为根的子树),总之套用树形 d p dp dp即可解决。因此可以做到 O ( n n ) O(n\sqrt{n}) O(nn )

那么 k = 1 k=1 k=1呢?应当注意到,当连通块数目为 x x x时,每个连通块的大小只能是 ⌊ n x ⌋ , ⌈ n x ⌉ \lfloor\frac{n}{x}\rfloor,\lceil\frac{n}{x}\rceil xn,xn两种情况,因此也不超过 n \sqrt{n} n

还是考虑树的情况,因为我们已经枚举了 t t t因此事实上没有那么复杂,将子树大小和 t t t简单比较一下即可。

最后简单容斥一下,可以做到复杂度 O ( n n ) O(n\sqrt{n}) O(nn )

原来代码这么好写,我是纯纯的丝薄。

#include<bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f using namespace std; const int mod=998244353; const int N=2e5+5; int n,m,K; int dfn[N],low[N],vis[N],num,cnt,in[N]; ll res; stack<int>s; vector<int>g[N]; //fixed vector<int>G[N]; void add(ll &x,ll y){ x=(x+y)%mod; } void addedge(int x,int y){ G[x].pb(y),G[y].pb(x); in[max(x,y)]++; } void tarjan(int u){ dfn[u]=low[u]=++num,vis[u]=1,s.push(u); for(auto v:g[u]){ if(!dfn[v]){ tarjan(v),low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ cnt++; int tmp=0; do{ tmp=s.top();s.pop(); vis[tmp]=0; addedge(tmp,cnt); }while(tmp!=v); addedge(u,cnt); } } else if(vis[v])low[u]=min(low[u],dfn[v]); } } int sz[N],root; void findroot(int u,int topf){ sz[u]=(u<=n); for(auto v:G[u]){ if(v!=topf){ findroot(v,u),sz[u]+=sz[v]; } } if(!root&&sz[u]>=(n+1)/2)root=u; } //fixed ll dfs(int u,int topf,int X){ if(u<=n&&sz[u]%X)return 0; if(u>n){ ll res=1; for(auto v:G[u]){ if(v!=topf){ res=res*dfs(v,u,X)%mod; } if(res==0)return 0; } return res; } ll res=1;int tot=1; for(auto v:G[u]){ if(v!=topf){ if(sz[v]>=X){ res=res*dfs(v,u,X)%mod; } else{ tot+=sz[v]; } if(res==0||tot>X)return 0; } } if(tot!=X)return 0; return res; } int vs[N]; ll dfs2(int u,int topf,int X){ if(u>n){ ll res=1; for(auto v:G[u]){ if(v!=topf){ res=res*dfs2(v,u,X)%mod; } if(res==0)return 0; } return res; } ll res=1,ans=0;int tot=1,tot2=0; for(auto v:G[u]){ if(v!=topf){ if(sz[v]>=X+1){ res=res*dfs2(v,u,X)%mod; } else if(sz[v]==X&&in[v]==2)tot2++; else tot+=sz[v]; } } if(tot>=X&&tot<=X+1){ add(ans,res); } if(tot==1){ add(ans,tot2*res); } return ans; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m>>K,cnt=n; for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; g[x].pb(y),g[y].pb(x); } tarjan(1); findroot(1,0); findroot(root,0); //fixed if(K==0){ for(int i=1;i<n;i++){ add(res,dfs(root,0,i)); } } //fixed else{ for(int i=2;i<=n;i++){ vs[n/i]=1; } for(int i=1;i<n;i++){ if(vs[i]){ add(res,dfs2(root,0,i)); add(res,-dfs(root,0,i+1)); } else{ add(res,dfs(root,0,i)); } } } cout<<(res+mod)%mod; }

__EOF__

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