Description
給一個n個點m條邊的無向圖,每個點有個權值0或1。q次詢問x,y求是否存在一條從x到y的路徑使得經過節點的權值連接起來是一個迴文串
Solution
這個是loj加強過的數據。。原題好像是3e3的
m2的做法就是按照原圖dp,設f[x,y]表示x到y有一條合法路徑,枚舉x和y的鄰邊轉移就可以做到m2。實現的時候開一個隊列,像bfs一樣把新增的合法路徑扔進隊尾就可以了
可以發現瓶頸在於邊數太多了。考慮按照顏色把邊分成3類,嘿嘿、嘿掰、掰掰邊。可以發現現在經過一個連通塊的路徑顏色都是一樣的,這樣就只和路徑長度相關了。由於我們可以反覆經過一個點,那麼就只和奇偶性有關了。
考慮按照是否存在奇環把連通塊分兩類。
若不存在奇環,那麼我們可以把連通塊黑白染色,一條從黑點到白點的路徑長度的奇偶性就是確定的。所以我們只需要保留原連通塊的連通性,原本繞圈的操作可以通過反覆橫跳湊夠。
若存在奇環,那麼就會出現奇偶性改變的情況,這個時候我們只需要隨便找個點x連一個自環就行了。可以發現這樣就能實現反覆橫跳的同時改變奇偶性。
然後我們就成功地把邊數降到了O(n)級別,套用上面那個做法就可以A辣
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=2005;
const int E=50005;
std:: vector <int> G[N];
std:: queue <int> que;
struct Graph {
struct edge {int x,y,next;} e[E];
int d[N],ls[N],edCnt;
int c[N],flag;
void add_edge(int x,int y) {
e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}
void dfs(int x,int id) {
c[x]=id;
for (int i=ls[x];i;i=e[i].next) {
if (c[e[i].y]==-1) {
dfs(e[i].y,!id);
G[x].push_back(e[i].y);
G[e[i].y].push_back(x);
} else if (c[e[i].y]==id) flag=true;
}
}
void build(int n) {
rep(i,1,n) c[i]=-1;
rep(i,1,n) if (c[i]==-1) {
flag=false;
dfs(i,1);
if (flag) G[i].push_back(i);
}
}
} BB,BW,WW;
bool f[N][N];
char s[N];
void ins(int x,int y) {
if (x>y) std:: swap(x,y);
if (f[x][y]) return ;
que.push(x),que.push(y);
f[x][y]=f[y][x]=1;
}
int main(void) {
freopen("data.in","r",stdin);
int n,m,k; scanf("%d%d%d",&n,&m,&k);
scanf("%s",s+1);
rep(i,1,n) ins(i,i);
rep(i,1,m) {
int x,y; scanf("%d%d",&x,&y);
if (s[x]==s[y]) {
if (s[x]=='1') BB.add_edge(x,y);
else WW.add_edge(x,y);
ins(x,y);
} else BW.add_edge(x,y);
}
BB.build(n),WW.build(n),BW.build(n);
for (;!que.empty();) {
int x=que.front(); que.pop();
int y=que.front(); que.pop();
for (int i=0;i<G[x].size();++i) {
for (int j=0;j<G[y].size();++j) {
int a=G[x][i],b=G[y][j];
if (s[a]==s[b]) ins(a,b);
}
}
}
for (;k--;) {
int x,y; scanf("%d%d",&x,&y);
puts(f[x][y]?"YES":"NO");
}
return 0;
}