【Comet OJ - Contest #15 G 孤獨的吉姆 6】【圖論】

題意

給一個nn個點mm條邊的簡單無向連通圖,要刪掉一些邊,使得度數爲奇數的點儘可能多。輸出長度爲mm0101串,00表示刪掉第ii條邊,11表示不刪,要求輸出字典序最大的方案。
n6105,m9105n\le6*10^5,m\le9*10^5

分析

比賽結束後幾分鐘調過了這題,錯失了AK的大好機會。
對於兩個能夠互相到達的點,我們可以通過對兩點間某條路徑上邊的狀態取反,來改變這兩個點的奇偶性,所以最後要麼所有點的度數都是奇數,要麼只有一個點的度數是偶數。問題在於如何最大化答案字典序。
如果我們按邊的編號求最大生成樹,每次隨便選兩個度數爲偶數的點,並改變它們樹上路徑中邊的狀態,就能保證編號較小的邊在答案中的狀態必然是11
可以發現在樹的形態固定的條件下,無論按何種方式配對,最終的結果都是一樣的。
這時如果所有點的度數都是奇數,那麼只需要求出所有邊的最終狀態就做完了。
如果有一個點的度數是偶數,則還能改變從這個點開始到任意一個點路徑上邊的狀態。這裏我的做法是以該點爲根dfs求出每條邊到根路徑上第一個編號小於它的邊並在兩者間連邊,從而得到一棵新的樹,然後在新的樹上貪心。
時間複雜度O(n+m)O(n+m).

代碼

#include<bits/stdc++.h>
#define mp std::make_pair
#define pb push_back

typedef std::pair<int,int> pi;

const int N=600005;
const int M=900005;

int n,m,f[N],h[N],ans[N*2],deg[N],ID[N],FA[N],top,top1,sta[N],sta1[N],dep[N];
std::vector<pi> e[N];
std::vector<int> vec,ee[M];
struct data{int x,y;}a[M];

void dfs(int x,int fa)
{
	int id=0;
	dep[x]=dep[fa]+1;
	for (auto to:e[x])
		if (to.first!=fa) dfs(to.first,x),h[x]^=h[to.first];
		else id=to.second;
	if (h[x]) ans[id]^=1;
	FA[x]=fa;ID[x]=id;
}

int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}

void work(int x,int fa)
{
	int id=ID[x],tmp=top1;
	if (id)
	{
		while (top&&sta[top]>id) sta1[++top1]=sta[top--];
		ee[sta[top]].pb(id);
		sta[++top]=id;
	}
	for (auto to:e[x])
		if (to.first!=fa) work(to.first,x);
	if (id) top--;
	while (top1>tmp) sta[++top]=sta1[top1--];
}

void dfs1(int x)
{
	int mn=m+1;
	for (auto to:ee[x])
		if (!ans[to]) mn=std::min(mn,to);
	if (!x&&mn==m+1) return;
	if (mn==m+1)
	{
		int u=a[x].x,v=a[x].y;
		if (dep[v]>dep[u]) u=v;
		while (ID[u]) ans[ID[u]]^=1,u=FA[u];
	}
	else dfs1(mn);
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y),a[i].x++,a[i].y++,ans[i]=1,deg[a[i].x]^=1,deg[a[i].y]^=1;
	for (int i=1;i<=n;i++) f[i]=i;
	for (int i=m;i>=1;i--)
	{
		int x=find(a[i].x),y=find(a[i].y);
		if (x==y) continue;
		f[x]=y;e[a[i].x].pb(mp(a[i].y,i));e[a[i].y].pb(mp(a[i].x,i));
	}
	for (int i=1;i<=n;i++) if (!deg[i]) vec.pb(i);
	for (int j=1;j<vec.size();j+=2) h[vec[j]]^=1,h[vec[j-1]]^=1;
	if (vec.size()%2==0) dfs(1,0);
	else
	{
		int rt=vec.back();
		dfs(rt,0);
		ee[0].clear();
		work(rt,0);
		dfs1(0);
	}
	for (int i=1;i<=m;i++) printf("%d",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章