題目
分析
考慮最plain的DP,表示到是否有一條迴文路徑,如果,那麼(爲的鄰結點,爲的鄰結點,且)。用Bfs
的順序完成這個DP即可,大約是這樣:
queue<Edge> Q;
int Ans[MAXN+5][MAXN+5];
inline void Push(Edge e){
Q.push(e);
Ans[e.u][e.v]=Ans[e.v][e.u]=1;
}
for(int i=1;i<=M;i++)
Push(E[i]);
while(!Q.empty()){
int u=Q.front().u,v=Q.front().v;Q.pop();
for(int i=0;i<int(G[u].size());i++){
int x=G[u][i];
for(int j=0;j<int(G[v].size());j++){
int y=G[v][j];
if(!Ans[x][y]&&Num[x]==Num[y])
Push((Edge){x,y});
}
}
}
於是我們喜聞樂見地發現,對於每條邊最壞情況下會往外擴展另外的所有邊,於是時間複雜度就成了,TLE,據說有30分。
然後,既然邊太多了,我們就嘗試砍掉一些邊,使新圖和原圖對題意等價。
(由於刪掉一些邊後,原本爲的現在肯定也爲,所以我們只需要讓原本爲的不會變成即可)
對於一條路徑,它一定是由若干段相同權值的點構成的(下圖中點上的數字代表權值):
如果這條路徑是迴文的,則,。
注意題面里加粗的字,一條邊是可以反覆使用的,也就是說,如果,我們完全可以把一條邊多走一遍,那就由“”變成了“”,這樣一來就等於了。
於是得到一個結論:只要和、和……奇偶性相同,這條路徑就可以視爲迴文的。
好了,圖中的奇偶性問題,果斷考慮二分圖。
對於一個只由相同權值的點(例如,)組成的連通塊,分類討論:
- 若它是二分圖,那原來在這個聯通塊裏面就一定奇數或偶數條邊的路徑之一:
例如原來某條迴文路徑如圖所示,左進右出,那這條路徑在該二分圖裏面,不管怎麼走,一定都是奇數長度。也就是說,只要這個二分圖是聯通的,原路徑2就可以轉化成奇偶性相同的另一條路徑。那麼我們對該二分圖做一個生成樹,是不會影響中的值的,邊數大大減少。 - 如果不是二分圖,原來在這個聯通塊裏面就一定是奇數、偶數條邊的路徑都可以,所以做完生成樹後隨便找一個點,加個自環,就能保證奇數偶數長度的路徑一定都有了,邊數同樣大大減少。
現在其實還有一類邊:第一張圖中的橘色邊,這樣的邊怎麼連是沒有影響的(我們考慮的是點,考慮按同點權的點縮點後,這樣的邊一定是單個出現的,所以沒有影響),所以直接生成樹。
代碼
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+(c^48),c=getchar();
return x;
}
#define MAXN 5000
#define MAXM 500000
#define MAXT 100000
int N,M,T;
int Num[MAXN+5];
char str[MAXN+5];
int Fa[MAXN+5];
int Find(int u){
return Fa[u]==u?u:Fa[u]=Find(Fa[u]);
}
struct Edge{
int u,v;
}E[MAXM+5];
vector<int> G[MAXN+5],H[MAXN+5];
queue<Edge> Q;
int Ans[MAXN+5][MAXN+5];
inline void Push(Edge e){
Q.push(e);
Ans[e.u][e.v]=Ans[e.v][e.u]=1;
}
int Col[MAXN+5];
bool Check(int u,int c){
Col[u]=c;
bool ret=1;
for(int i=0;i<int(H[u].size());i++){
int v=H[u][i];
if(Num[v]==Num[u]){
if(!Col[v]){
G[u].push_back(v);
G[v].push_back(u);
Push((Edge){u,v});
ret&=Check(v,c^1);
}
else if(Col[v]==c)
ret=0;
}
}
return ret;
}
int main(){
N=read(),M=read(),T=read();
scanf("%s",str+1);
for(int i=1;i<=N;i++)
Num[i]=str[i]-'0';
for(int i=1;i<=M;i++){
int u=read(),v=read();
E[i].u=u,E[i].v=v;
H[u].push_back(v);
H[v].push_back(u);
}
for(int i=1;i<=N;i++)
Fa[i]=i;
for(int i=1;i<=N;i++)
if(!Col[i]){
if(!Check(i,2))
G[i].push_back(i);
}
for(int i=1;i<=M;i++){
int u=E[i].u,v=E[i].v;
if(Num[u]!=Num[v]&&Find(u)!=Find(v)){
Fa[Find(u)]=Find(v);
G[u].push_back(v);
G[v].push_back(u);
}
}
for(int i=1;i<=N;i++)
Push((Edge){i,i});
while(!Q.empty()){
int u=Q.front().u,v=Q.front().v;Q.pop();
for(int i=0;i<int(G[u].size());i++){
int x=G[u][i];
for(int j=0;j<int(G[v].size());j++){
int y=G[v][j];
if(!Ans[x][y]&&Num[x]==Num[y])
Push((Edge){x,y});
}
}
}
while(T--)
puts(Ans[read()][read()]?"YES":"NO");
}