給定n
nn個點,m
mm條邊的圖。每個點有0/1
0/10/
1的標號,有q
qq個詢問,每次詢問點對(u,
v)
(u,v)
(u,v
)間是否一條路徑(不一定是簡單路徑),滿足路徑經過的點的標號所形成的串是回文串。
n
≤5000,m
≤5000000
n \leq 5000, m \leq 5000000
n≤5000
,m≤5
0000
00 設fi,
jf_
fi,j
表示i,j
i,ji,
j是否存在一條滿足條件的路徑。初始狀態和轉移顯然。
考慮每次轉移的複雜度太高,而這張圖的點數較少,邊數較多。
把連線了同一種標號的邊拎出來。可以發現這些邊使圖形成了若干連通塊。對於某個連通塊,如果它是二分圖,那麼進行黑白染色後,同色點之間路徑的長度只可能為奇數,異色點之間路徑長度只可能為偶數。
不難發現,把這張二分圖替換為它的一棵生成樹,上述性質不變,而乙個端點在這個連通塊的狀態的轉移也不變,因為經過原圖上的一條邊可以替代為經過這條非樹邊在樹上的路徑(讓另乙個端點重複走即可)。而如果這不是二分圖,意味著可以可以經過某個環改變路徑的奇偶性,所以給這棵生成樹加上乙個自環。
對於連線不同標號的邊同理。經過這樣操作之後,邊數不超過2n−
22n-2
2n−2
,直接套用上述暴力dpdp
dp即可。
#include
using
namespace std;
const
int maxn =
5005
, maxm =
500005
;int n, m, q;
vectorint,
int>> e[2]
[2];
int fa[maxn]
, col[maxn]
, f[maxn]
[maxn]
;vector<
int> to[maxn][2
];char s[maxn]
;queueint,
int>> que;
struct edge
e[maxm *4]
;int h[maxn]
, tot;
inline
intgi()
inline
void
add(
int u,
int v)
; h[u]
= tot;
e[++tot]
=(edge)
; h[v]
= tot;
to[u]
[s[v]
-'0'].
push_back
(v);
to[v]
[s[u]
-'0'].
push_back
(u);
}int
find
(int x)
bool
merge
(int x,
int y)
void
dfs(
int u,
int fa)
void
solve
(int c1,
int c2)
for(
int i =
1; i <= n;
++i)
if(fa[i]
== i) col[i]=0
,dfs
(i,0);
for(
auto e : e[c1]
[c2]
)for
(int i =
1; i <= n;
++i)
if(col[i]==-
1)add(i, i);}
void
bfs(
) siz1 = to[u][1
].size()
; siz2 = to[v][1
].size()
;for
(i =
0; i < siz1;
++i)
for(j =
0; j < siz2;
++j)}}
intmain()
solve(0
,0);
solve(1
,1);
solve(0
,1);
bfs();
for(
int a, b, i =
1; i <= q;
++i)
return0;
}
HNOI2019 校園旅行
人生第一道黑題祭 本題偏重思維 判斷回文可以考慮它的遞迴定義 只有乙個字元的串是回文串。只有兩個字元的串,如果這兩個字元相同,也是回文串 如果 s 是回文串,那麼在 s 的開頭和末尾插入乙個相同的字元,形成的新串也是回文串。乙個可以想到的方法是設 f 表示從 x 到 y 可不可行 然後 text 列...
HNOI2019 校園旅行
某學校的每個建築都有乙個獨特的編號。一天你在校園裡無聊,決定在校園內隨意地漫步。你已經在校園裡呆過一段時間,對校園內每個建築的編號非常熟悉,於是你情不自禁的把周圍每個建築的編號都記了下來 但其實你沒有真的記下來,而是把每個建築的編號除以 2 取餘數得到 0 或 1,作為該建築的標記,多個建築物的標記...
HNOI2019 校園旅行 構造 生成樹 動規
hnoi2019 校園旅行 最樸素的做法就是點對擴充套件 o m 2 發現 n 比較小,我們是否能從 n 下手減少邊數呢?是肯定的 單獨看乙個顏色的聯通塊,如果是二分圖,我們生產樹和原來的效果相同 如果不是二分圖,是會有乙個環的,在樹上隨便圈乙個自環和原來的效果相同 而看不同顏色的連邊,一定是二分圖...