NOIP2022T3建造军营题解
NOIP2022T3建造军营题解
[NOIP2022] 建造军营
题目描述
A 国与 B 国正在激烈交战中,A 国打算在自己的国土上建造一些军营。
A 国的国土由
众所周知,军营之间的联络是十分重要的。然而此时 A 国接到情报,B 国将会于不久后袭击 A 国的一条道路,但具体的袭击目标却无从得知。如果 B 国袭击成功,这条道路将被切断,可能会造成 A 国某两个军营无法互相到达,这是 A 国极力避免的。因此 A 国决定派兵看守若干条道路(可以是一条或多条,也可以一条也不看守),A 国有信心保证被派兵看守的道路能够抵御 B 国的袭击而不被切断。
A 国希望制定一个建造军营和看守道路的方案,使得 B 国袭击的无论是 A 国的哪条道路,都不会造成某两座军营无法互相到达。现在,请你帮 A 国计算一下可能的建造军营和看守道路的方案数共有多少。由于方案数可能会很多,你只需要输出其对
输入格式
第一行包含两个正整数
接下来
输出格式
输出一行包含一个整数,表示建造军营和看守道路的方案数对
对所有数据,保证
题解
EX的计数题
显然先缩点,每个边双连通分量内部都是随便选边和选点的。由于这是一张无向连通图,所以缩点后是一棵树,对于计数题,不难想到是树形DP。考虑如何求解:
首先,由于缩点后建立的虚树要考虑每一个点所表示的边双连通分量的边数和点数,很烦,怎么办?也许我们可以采用某些方法使得我们只需要考虑一个?不难发现,我们可以先对总的方案数除以
接着,考虑如何树形DP。最简单直接的套路是设
然后我们考虑转移,设
前一节是之前的答案,后面两个式子是算上
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 5000050
#define p 1000000007
int edcc,c[N],dfn[N],low[N],ans,a[N],num,tot=1,cnt,n,m,head[N],nxt[N],ver[N],inv=(p+1)>>1,bridge[N];
int shead[N],snxt[N],sver[N],f[N],stot=1;
void add_s(int u,int v){
snxt[++stot]=shead[u],sver[shead[u]=stot]=v;
}
void add(int u,int v){
nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void dfs(int u,int fa){
ans=(ans+f[u])%p;
for(int i=shead[u];i;i=snxt[i]){
int v=sver[i];
if(v!=fa){
dfs(v,u);
f[v]=(1ll*f[v]*inv)%p;
ans=(1ll*ans+1ll*f[u]*f[v]%p)%p;
f[u]=(1ll*f[u]+1ll*f[v]+1ll*f[u]*f[v]%p)%p;
}
}
}
void tarjan(int u,int in){
// cout<<u<<endl;
dfn[u]=low[u]=++num;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(!dfn[v]){
tarjan(v,i);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]){
bridge[i]=bridge[i^1]=1;
}
}
else if(i!=(in^1))low[u]=min(low[u],dfn[v]);
}
// cout<<u<<endl;
}
void dfs2(int u,int num){
c[u]=num;
for(int i=head[u];i;i=nxt[i]){
if(bridge[i]||c[ver[i]])continue;
dfs2(ver[i],num);
}
}
void init_1(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
for(int i=1;i<=n;i++)if(!c[i])dfs2(i,++edcc);
}
void init_2(){
for(int i=1;i<=edcc;i++)f[i]=1;
for(int i=1;i<=n;i++)f[c[i]]=1ll*f[c[i]]*2%p;
for(int i=1;i<=edcc;i++)f[i]--;
for(int i=2;i<=tot;i+=2){
int u=ver[i],v=ver[i^1];
if(c[u]==c[v])continue;
add_s(c[u],c[v]);
add_s(c[v],c[u]);
}
// cout<<edcc<<"\n";
dfs(1,0);
for(int i=1;i<=m;i++)ans=1ll*ans*2%p;
cout<<(ans%p+p)%p<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
// freopen("barrack4.in","r",stdin);
init_1();
// cout<<"AS"<<endl;
init_2();
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战