P4494-[HAOI2018]反色游戏【圆方树】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P4494


1|1题目大意

给出n个点m条边的一张无向图,节点有0/1,每条边可以选择是否取反两边的点。

开始求将所有节点变为0的方案,然后对于每个点询问删去这个点之后的方案

1T5,1n,m105


1|2解题思路

图的比较麻烦,先考虑树上的,那么每条边取不取反取决于它连接的子节点的黑白,但是根节点却无法这么调整。所以如果黑色个数为奇数个那么方案为0,否则方案为1

然后考虑一张连通图,考虑对于图中的一个生成树来说,无论非生成树上的边是否取反,都可以用这棵生成树调整回来,也就是如果黑色为奇数个方案为0,否则方案为2mn+1

因为原图不一定连通,设连通块个数为k,那么第一问答案就是2mn+k(每个连通块的黑色个数为奇数个)。

然后第二问,其实就是去掉这条边之后会分割一个连通块以影响答案。

建立广义圆方树,统计每个点删去后会多产生的连通块数量以及是否有分割出来的连通块的黑色个数为奇数。

顺带一提的是需要特判如果有两个或者以上的连通块黑色为奇数个,那么全都无解,否则只有可能删除掉黑色奇数连通块里的点。

时间复杂度O(Tn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<stack> #include<vector> #define ll long long using namespace std; const ll N=2e5+10,P=1e9+7; ll Z,n,m,dfc,sum,cnt,st[N],deg[N]; ll dfn[N],low[N],pw[N],siz[N]; bool tag[N],nok[N],v[N]; stack<ll> s;char t[N]; vector<ll>G[N],T[N]; void tarjan(ll x){ dfn[x]=low[x]=++dfc;sum+=(t[x]=='1'); s.push(x);st[++st[0]]=x; for(ll i=0;i<G[x].size();i++){ ll y=G[x][i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); if(low[y]==dfn[x]){ ll k;++cnt; do{ k=s.top();s.pop();deg[k]--; T[cnt].push_back(k); T[k].push_back(cnt); }while(k!=y); T[cnt].push_back(x); T[x].push_back(cnt); deg[x]--; } } else low[x]=min(low[x],dfn[y]); } return; } void dfs(ll x){ v[x]=1;st[++st[0]]=x; siz[x]=(x<=n)&(t[x]=='1'); for(ll i=0;i<T[x].size();i++){ ll y=T[x][i]; if(v[y])continue; dfs(y);siz[x]+=siz[y]; if(siz[y]&1)nok[x]=1; } return; } signed main() { scanf("%lld",&Z);pw[0]=1; for(ll i=1;i<N;i++)pw[i]=pw[i-1]*2%P; while(Z--){ dfc=0; memset(deg,0,sizeof(deg)); memset(nok,0,sizeof(nok)); memset(tag,0,sizeof(tag)); memset(dfn,0,sizeof(dfn)); memset(v,0,sizeof(v)); while(!s.empty())s.pop(); scanf("%lld%lld",&n,&m); for(ll i=1;i<=2*n;i++) T[i].clear(),G[i].clear(); for(ll i=1;i<=m;i++){ ll x,y;scanf("%lld%lld",&x,&y); G[x].push_back(y);deg[x]++; G[y].push_back(x);deg[y]++; } scanf("%s",t+1);cnt=n; ll one=0,k=0; for(ll i=1;i<=n;i++){ if(dfn[i])continue; st[0]=sum=0; tarjan(i);k++; if(sum&1){ for(ll j=1;j<=st[0];j++) tag[st[j]]=1; one++; } } if(one>1){ for(ll i=0;i<=n;i++)printf("0 "); putchar('\n');continue; } else if(one)printf("0 "); else printf("%lld ",pw[m-n+k]); for(ll i=1;i<=n;i++){ if(v[i])continue; st[0]=0;dfs(i); for(ll j=1;j<=st[0];j++) if((siz[i]-siz[st[j]])&1)nok[st[j]]=1; } for(ll i=1;i<=n;i++) if(nok[i]||(one&&!tag[i]))printf("0 "); else printf("%lld ",pw[m-n+k-deg[i]]); putchar('\n'); } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14601846.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(110)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示