LOJ3057[HNOI2019]校园旅行【dp+优化建图】

mac2024-03-28  31

LOJ3057


updated 11.7 发现在UOJ上TLE。改进了代码实现。 生成树是可以直接做的,减少了常数,这样每一颗树最多连一条自环,加一个标记即可。


SOL

30pts

从构造路径的角度思考,难以入手。总点数能实现 O ( n 2 ) O(n^2) O(n2)算法,转换思路,从中间向两边接点,构造回文串。

f [ i ] [ j ] f[i][j] f[i][j]表示 i , j i,j i,j点对能否作为回文路径的两端。故枚举 i , j i,j i,j的边尝试转移。

不难发现这样做总复杂度是 O ( m 2 ) O(m^2) O(m2)(每一对"边"最多被拿出两次)

100pts

优化建图,目标是将边数减少到 O ( n ) O(n) O(n)级别。

先给出做法,再尝试证明。

做法:

我们把边分成两种,一种是两端点的颜色相同,另一种是不同。

对于前一种边,会形成若干个连通块。对于每一个连通块,如果是二分图,就做一颗生成树取代原连通块的边;如果不是就再随便找一个点加一个自环。

对于后一种边形成的连通块,由于颜色已经染好了,一定是一个二分图,做一颗生成树替代。

(前一种会形成多个互相独立的连通块,后一个连接各个连通块。)

证明:

我们要让原来的合法路径依然合法,且不会产生新的合法路径。

首先对于一条合法的路径,我们可以从中心划分成若干段,为 0 / 1 0/1 0/1交替段,全是 0 / 1 0/1 0/1段两种。更改原图后,我们只需要让关于中点相对称的两端依然能匹配(设为 A A A, B B B两段)。

原来路径依然合法。

1.具体的长度不用考虑,只需要考虑段长的奇偶性。

由于点边可以重复经过,若 A A A段短了,可以重复走点走回来增加长度。但重复走点不会改变奇偶性。

2.对于一个同色连通块,只要两点能连通,且路径奇偶性不变即可

故如果有环,二分图中不同路径奇偶性不变,但是非二分图奇偶性要改变,多加一个自环就可以实现。

3.对于一个不同色连通块同理。

4.同色非二分图连通块中的任意一个点都可加自环,且可加任意多个(至少一个)

由2显然。 注意若加自环的点刚好也在另一个不同色连通块 S S S中,不会对 S S S产生影响。因为走两遍该点就不属于不同色段了。

原来不合法路径依然不合法

由上述证明,该建图方式利用的合法路径奇偶性的特点。 若原不合法路径奇偶性不合法,建图后奇偶性依然不合法。


CODE

#include<bits/stdc++.h> using namespace std; #define sf scanf #define ri register int #define in red() #define gc getchar() #define cs const #define ll long long inline int red(){ int num=0,f=1;char c=gc; for(;!isdigit(c);c=gc)if(c=='-')f=-1; for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48); return num*f; } cs int N=5e3+10,M=5e5+10; int n,m,Q; bool f[N][N],col[N]; vector<int>g[N]; struct edge{ int u,v; }e[M]; struct BCJ{ int fa[N],d[N],ins[N]; inline int find(int u){ if(!fa[u])return u; int t=fa[u]; fa[u]=find(fa[u]); d[u]^=d[t]; return fa[u]; } inline void merge(int x,int y,int col){ int fx=find(x),fy=find(y); if(fx^fy){ d[fx]=d[x]^d[y]^1; fa[fx]=fy; g[x].push_back(y); g[y].push_back(x); ins[fy]|=ins[fx]; } else{ if(d[x]^d[y]^1)ins[fx]=1; } } }G[2]; queue<edge> q; inline void solve(){ for(ri i=1;i<=n;++i)f[i][i]=1,q.push((edge){i,i}); for(ri u=1;u<=n;++u){ for(ri i=g[u].size()-1;i>=0;--i){ int v=g[u][i]; if(!f[u][v]&&col[u]==col[v]){ f[u][v]=f[v][u]=1; q.push((edge){u,v}); } } } while(!q.empty()){ edge t=q.front();q.pop(); int x=t.u,y=t.v; for(ri i=g[x].size()-1;i>=0;--i){ for(ri j=g[y].size()-1;j>=0;--j){ int vx=g[x][i],vy=g[y][j]; if(col[vx]==col[vy]&&!f[vx][vy]){ f[vx][vy]=f[vy][vx]=1; q.push((edge){vx,vy}); } } } } } char s[N]; signed main(){ n=in;m=in;Q=in; sf("%s",s+1); for(ri i=1;i<=n;++i){ col[i]=s[i]^48; } for(ri i=1;i<=m;++i){ e[i]=(edge){in,in}; } for(ri i=1;i<=m;++i){ G[col[e[i].u]^col[e[i].v]].merge(e[i].u,e[i].v,col[e[i].u]^col[e[i].v]); } for(ri i=1;i<=n;++i)if(G[0].find(i)==i&&G[0].ins[i])g[i].push_back(i); solve(); while(Q--){ int x=in,y=in; if(f[x][y])cout<<"YES\n"; else cout<<"NO\n"; } return 0; }
最新回复(0)