非常妙的一道思博题啊,不愧是myy出的题
首先我们考虑一个暴力DP,直接开一个数组\(f_{i,j}\)表示\(i\to j\)的路径能否构成回文串
考虑直接拿一个队列来转移,队列里存的都是\(f_{i,j}=1\)的点对,然后每次枚举两边的边更新答案并扩展即可
但是这样的复杂度是\(O(m^2)\)的,不够优秀。我们发现其实这种方法的复杂度瓶颈在于有很多无用边导致我们浪费了复杂度,因此我们考虑删去一些边
我们首先在原图上把所有同色点间的边连起来,由于每个点可以经过任意次,因此我们只需要考虑奇偶性
那么什么时候能让到一个点的奇偶性改变呢?其实很简单,就是存在奇环
我们再进一步思考,如果一个图没有奇环,那么它就是二分图,而二分图的一个生成树显然也满足如上的性质
那么也就意味着,如果此时构成的图是二分图,那么我们求出它的生成树即可
但是如果不是呢,其实也很简单,我们考虑最简单的奇环是什么,其实就是自环
所以这种情况我们可以直接找一个点给它连一个自环帮助转移即可
那么剩下的只有异色点之间的边了,这个由于它都帮你染色好了,所以必然是二分图,直接求生成树即可
综上,此时的边数只有\(O(n)\)级别,因此总复杂度就是\(O(n^2)\)
CODE
#include<cstdio>
#include<utility>
#include<cstring>
#define RI register int
#define CI const int&
#define Ms(f,x) memset(f,x,sizeof(f))
using namespace std;
const int N=5005,M=500005;
typedef pair <int,int> pi;
struct edge
{
int to,nxt;
}e[M<<1],ne[M<<1]; bool f[N][N],flag; pi q[N*N];
char s[N]; int n,m,t,H,T,head[N],nhead[N],cnt,x,y,col[N];
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
inline void nadd(CI x,CI y)
{
ne[++cnt]=(edge){y,nhead[x]}; nhead[x]=cnt;
}
inline void push(CI x,CI y)
{
f[x][y]=f[y][x]=1; q[++T]=make_pair(x,y);
}
#define to e[i].to
inline void DFS(CI now,CI tp)
{
for (RI i=head[now];i;i=e[i].nxt) if ((s[now]==s[to])==tp)
{
if (~col[to]) { if (col[to]==col[now]) flag=0; }
else col[to]=col[now]^1,DFS(to,tp),nadd(now,to),nadd(to,now);
}
}
#undef to
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d%d%d%s",&n,&m,&t,s+1),i=1;i<=m;++i)
scanf("%d%d",&x,&y),addedge(x,y),s[x]==s[y]&&(push(x,y),0); cnt=0; Ms(col,-1);
for (i=1;i<=n;++i) if (!~col[i]&&(col[i]=flag=1,DFS(i,1),!flag)) nadd(i,i);
for (Ms(col,-1),i=1;i<=n;++i) if (!~col[i]) col[i]=1,DFS(i,0);
for (i=1;i<=n;++i) push(i,i); while (H<T)
{
++H; int x=q[H].first,y=q[H].second;
for (i=nhead[x];i;i=ne[i].nxt) for (j=nhead[y];j;j=ne[j].nxt)
if (s[ne[i].to]==s[ne[j].to]&&!f[ne[i].to][ne[j].to]) push(ne[i].to,ne[j].to);
}
while (t--) scanf("%d%d",&x,&y),puts(f[x][y]?"YES":"NO"); return 0;
}