P7293 题解

1|0P7293 题解

传送门

1|1思路

提供一个不太一样的容斥做法。

首先容易发现答案只和每个点到 1 号点的奇偶最短路有关,可以先 O(n) 求出来。

然后考虑枚举距离 d,计算有多少个 K 元组的距离为 d。不妨设 d 为奇数,那么条件就是:

  1. 每个点的奇最短路的最大值为 d
  2. 存在一个点的偶最短路 >d

考虑怎么快速维护。对于每一张图,我们求出能到走到的奇偶最短路的最大值 maxd

我们把图分成两部分,一部分是 maxd<d,另一部分是 maxdd

对于第一部分,不存在满足第一个条件的点,而且对于所有 maxd<dd,这张图的满足第二个条件的点数量不会变,贡献很好维护。

对于第二部分,我们遍历所有满足条件的图,维护 f[0/1][0/1] 表示是否满足第一个条件,是否满足第二个条件的方案数。我们需要知道对于每一张图,有多少个点满足奇最短路的为 d,有多少个点满足奇最短路的不超过 d,有多少个点满足奇最短路不超过 d 且偶最短路 >d,这些信息都可以 O(n+maxd) 求出。最终转移是一个或卷积。这一部分复杂度是 maxd=O(n) 的。

总复杂度 O(n)

代码:

int K; int p1[N<<1],p2[N<<1]; struct Graph{ int n,m,cnt,maxn; vector<int>h,ver,nxt,dis[2],t,d,c,pre; inline void add_edge(int x,int y){cnt++;ver[cnt]=y;nxt[cnt]=h[x];h[x]=cnt;} inline void init(){ n=read(),m=read(); h.resize(n+1),ver.resize(2*m+1),nxt.resize(2*m+1),dis[0].resize(n+1),dis[1].resize(n+1); for(int i=1;i<=n;i++)dis[0][i]=dis[1][i]=mod; for(int i=1;i<=m;i++){ int x=read(),y=read(); add_edge(x,y),add_edge(y,x); } queue<int>q; dis[0][1]=0; q.push(2); while(!q.empty()){ int x=q.front()>>1,k=q.front()&1; q.pop(); for(int i=h[x];i;i=nxt[i]){ int y=ver[i]; if(dis[k^1][y]>dis[k][x]+1){ dis[k^1][y]=dis[k][x]+1; q.push((y<<1)|(k^1)); } } } for(int i=1;i<=n;i++){ if(dis[0][i]!=mod)maxn=max(maxn,dis[0][i]); if(dis[1][i]!=mod)maxn=max(maxn,dis[1][i]); } t.resize(maxn+1),d.resize(maxn+1),c.resize(maxn+3),pre.resize(maxn+1); for(int i=1;i<=n;i++){ if(dis[0][i]<dis[1][i]){ d[dis[0][i]]++; c[dis[0][i]]++; if(dis[1][i]!=mod)c[dis[1][i]+1]--; } if(dis[1][i]<dis[0][i]){ d[dis[1][i]]++; c[dis[1][i]]++; if(dis[0][i]!=mod)c[dis[0][i]+1]--; } if(dis[0][i]!=mod)t[dis[0][i]]++; if(dis[1][i]!=mod)t[dis[1][i]]++; } for(int i=0;i<=maxn;i++){ pre[i]=t[i]; if(i>1)pre[i]+=pre[i-2]; } for(int i=2;i<=maxn+2;i++)c[i]+=c[i-2]; p1[maxn+2]=1ll*p1[maxn+2]*pre[maxn]%mod; p2[maxn+2]=1ll*p2[maxn+2]*dec(pre[maxn],c[maxn+2])%mod; p1[maxn+1]=1ll*p1[maxn+1]*(maxn?pre[maxn-1]:0)%mod; p2[maxn+1]=1ll*p2[maxn+1]*dec(maxn?pre[maxn-1]:0,c[maxn+1])%mod; } }G[N]; vector<int>hav[N],q; int maxd; int f[4],g[4],a[4],ans; int main(){ K=read(); for(int i=0;i<=200003;i++)p1[i]=p2[i]=1; for(int i=1;i<=K;i++){ G[i].init(); hav[G[i].maxn].push_back(i); maxd=max(maxd,G[i].maxn); } for(int i=2;i<=maxd;i++)p1[i]=1ll*p1[i]*p1[i-2]%mod,p2[i]=1ll*p2[i]*p2[i-2]%mod; for(int i=maxd;i;i--){ for(auto j:hav[i])q.push_back(j); memset(f,0,sizeof(f)),memset(g,0,sizeof(g)); g[0]=p2[i];g[1]=dec(p1[i],p2[i]); for(auto j:q){ a[0]=G[j].pre[i]-G[j].t[i]-G[j].c[i]+G[j].d[i],a[1]=G[j].c[i]-G[j].d[i],a[2]=G[j].t[i]-G[j].d[i],a[3]=G[j].d[i]; memset(f,0,sizeof(f)); for(int i=0;i<4;i++){ for(int j=0;j<4;j++)add(f[i|j],1ll*g[i]*a[j]%mod); } swap(f,g); } add(ans,1ll*g[3]*i%mod); } cout<<ans<<endl; return 0; }

__EOF__

本文作者Xttttr
本文链接https://www.cnblogs.com/Xttttr/p/17959957.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Xttttr  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示