P8867 [NOIP2022] 建造军营 题解
边双缩点,记
设
初始有
考虑答案的统计:记
显然
接下来考虑转移,设当前儿子为
对于
对于
- 之前还没有选点
那么
- 之前已经选了
那么
注意
以上操作均可在
代码:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define gt getchar
#define pt putchar
#define fst first
#define scd second
typedef long long ll;
const int N=5e5+5;
const int M=1e6+5;
const int mod=1e9+7;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
inline bool __(char ch){return ch>=48&&ch<=57;}
template<class T> inline void read(T &x){
x=0;bool sgn=0;char ch=gt();
while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
if(sgn) x=-x;
}
template<class T,class ...T1> inline void read(T &x,T1 &...x1){
read(x);
read(x1...);
}
template<class T> inline void print(T x){
static char st[70];short top=0;
if(x<0) pt('-');
do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
print(x);
putchar(' ');
}
template<class T> inline void println(T x){
print(x);
putchar('\n');
}
int n,m,pw[N+M];
ll ans,f[N][2];
vector<int>g[N];
struct edge{
int to,nxt;
}e[M<<1];
int head[N],cnt=1;
inline void add_edge(int f,int t){
e[++cnt].to=t;
e[cnt].nxt=head[f];
head[f]=cnt;
}
inline void add_double(int f,int t){
add_edge(f,t);
add_edge(t,f);
}
int id[N],dfn[N],low[N],ti,cnt_dcc,E[N],V[N],s[N];
bool cut[M<<1];
void tarjan(int u,int from){
dfn[u]=low[u]=++ti;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(i==(from^1)) continue;
if(!dfn[v]){
tarjan(v,i);
if(dfn[u]<low[v]) cut[i]=cut[i^1]=1;
low[u]=min(low[u],low[v]);
}
else low[u]=min(low[u],dfn[v]);
}
}
void dfs(int u,int dcc_id){
id[u]=dcc_id;
V[dcc_id]++;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(id[v]||cut[i]) continue;
dfs(v,dcc_id);
}
}
void dfs_init(int u,int fa){
s[u]=E[u];
for(int v:g[u]){
if(v==fa) continue;
dfs_init(v,u);
s[u]+=(s[v]+1);
}
}
void dfs_dp(int u,int fa){
for(int v:g[u]){
if(v==fa) continue;
dfs_dp(v,u);
f[u][1]=(f[u][1]*(2*f[v][0]+f[v][1])%mod+f[u][0]*f[v][1]%mod)%mod;
f[u][0]=(f[u][0]*2*f[v][0])%mod;
}
if(u!=1) ans=(ans+f[u][1]*pw[s[1]-s[u]-1])%mod;
else ans=(ans+f[u][1])%mod;
}
signed main(){
read(n,m);
pw[0]=1;
for(int i=1;i<=n+m;++i) pw[i]=pw[i-1]*2%mod;
for(int u,v,i=1;i<=m;++i){
read(u,v);
add_double(u,v);
}
tarjan(1,0);
for(int i=1;i<=n;++i) if(!id[i]) dfs(i,++cnt_dcc);
for(int u=1;u<=n;++u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(id[u]==id[v]) E[id[u]]++;
else g[id[u]].emplace_back(id[v]);
}
}
for(int i=1;i<=cnt_dcc;++i){
E[i]>>=1;
f[i][0]=pw[E[i]];
f[i][1]=(pw[V[i]+E[i]]-pw[E[i]]+mod)%mod;
}
dfs_init(1,0);
dfs_dp(1,0);
println(ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!