loj#3057 「HNOI2019」校園旅行 dp

Description


給一個n個點m條邊的無向圖,每個點有個權值0或1。q次詢問x,y求是否存在一條從x到y的路徑使得經過節點的權值連接起來是一個迴文串
n5e3,m5e5n\le5e3,m\le5e5

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章