Luogu P14146 朝花 题解

P14146 朝花

是非常可爱的构造题喵,超喜欢这题的 Idea。

注意到部分分是所有点度数为偶数,考虑这种图都存在欧拉回路,于是利用欧拉回路进行构造。发现欧拉回路的一个环中,每次只操作 \(3\) 个点,固定一个点为环的开头,剩下两个点在环上顺着消,这两个点每次各往后移动一条边,一定可以消完一个环,且代价仅为 \(9m\)。这个操作在欧拉回路的序列上很容易实现。

考虑正解,上面做法非常优秀,思考如何消掉度数为奇数的点转化为部分分。考虑操作对于度数的影响,发现无论加边还是删边都会导致奇偶性刚好变化 \(1\),如果操作的点的数量为偶数,恰好会改变所有操作的点的度数的奇偶性。因此,我们可以利用上面操作一次消掉 \(4\) 个奇点,代价仅为 \(4n\)

由图论经典结论我们知道度数为奇数的点一定有偶数个,于是最终要么度数为奇数的点被消完,要么还剩两个。考虑消剩下的两个,等价于修改两个点之间的连边状态,设需要修改连边状态的点为 \(p_1,p_2\),由于 \(n\ge6\),所以一定能找到另外四个点 \(a_1,a_2,a_3,a_4\)

\(1\) 次操作 \(\{a_1,a_2,a_3,a_4\}\),第 \(2\sim 5\) 次操作 \(\{a_i,p_1,p_2\}\),其中 \(i\) 为本次操作为第 \(i+1\) 次操作,第 \(6\) 次操作 \(\{a_1,a_2,a_3,a_4,p_1,p_2\}\),完成目标,且不影响其他边的状态。代价仅为 \(88\)

我们用上面做法即可将问题转化为部分分问题。具体写代码的时候可以自己写一个函数模拟操作,把所有要做的操作都做完之后再建图跑欧拉回路,会好写一点。找另外四个点 \(a_1,a_2,a_3,a_4\) 时有点麻烦,注意一下。

时间复杂度 \(O(n+m)\),总代价 \(4n+9m+88\)

#include <bits/stdc++.h>
using namespace std;
int n,m,u[800000],v[800000],dg[200000],d[200000],h[200000],a[800000][6],ou[800000],c=0,cnt=0;
bool w[800000];
unordered_map<long long,bool>vis;
vector<int>p,s[200000],id[200000];
void add_edge(int u,int v)
{
	s[u].push_back(v);
	dg[u]++;
}

long long has(int x,int y)
{
	return 1ll*x*1000000+y;
}

void operate(int a[],int d)
{
	for(int i=0;i<d;i++)
	    for(int j=i+1;j<d;j++)
	        if(vis[has(a[i],a[j])]||vis[has(a[j],a[i])])vis[has(a[i],a[j])]=vis[has(a[j],a[i])]=0;
	        else vis[has(a[i],a[j])]=vis[has(a[j],a[i])]=1,u[++m]=a[i],v[m]=a[j];
}

void del4(int x,int y,int z,int w)
{
	int p1=x,p2=y,p3=z,p4=w;
	d[++c]=4,a[c][0]=p1,a[c][1]=p2,a[c][2]=p3,a[c][3]=p4,operate(a[c],d[c]);
}

void del6(int x,int y)
{
    int p1=x,p2=y,p[5]={0,1,2,3,4},now=4;
	for(int k=1;k<=4;k++)
		for(int i=1;i<=4;i++)
		    if(p[i]==p1||p[i]==p2)p[i]=++now;
	d[++c]=4,a[c][0]=p[1],a[c][1]=p[2],a[c][2]=p[3],a[c][3]=p[4],operate(a[c],d[c]);
	for(int i=1;i<=4;i++)d[++c]=3,a[c][0]=p[i],a[c][1]=p1,a[c][2]=p2,operate(a[c],d[c]);
	d[++c]=6,a[c][0]=p[1],a[c][1]=p[2],a[c][2]=p[3],a[c][3]=p[4],a[c][4]=p1,a[c][5]=p2,operate(a[c],d[c]);
}

void dfs(int x)
{
	for(int i=h[x];i<(int)s[x].size();i=h[x])
	    {
		h[x]++;
		if(vis[has(x,s[x][i])])vis[has(x,s[x][i])]=vis[has(s[x][i],x)]=0,dfs(s[x][i]);
	    }
	ou[++cnt]=x;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)scanf("%d%d",&u[i],&v[i]),vis[has(u[i],v[i])]=vis[has(v[i],u[i])]=1,dg[u[i]]++,dg[v[i]]++;
	for(int i=1;i<=n;i++)
	    if(dg[i]&1)p.push_back(i),cnt++;
	while(cnt>2)
	   {
	   	int d[5]={0,0,0,0,0};
	   	for(int i=1;i<=4;i++)d[i]=p.back(),p.pop_back();
	   	del4(d[1],d[2],d[3],d[4]),cnt-=4;
	   }
	if(cnt==2)
	   {
	   	int x=p.back(),y=0;
	   	p.pop_back(),y=p.back();
	   	del6(x,y),cnt-=2;
	   }
	for(int i=1;i<=n;i++)dg[i]=0;
	for(int i=1;i<=m;i++)
	    if(vis[has(u[i],v[i])])add_edge(u[i],v[i]),add_edge(v[i],u[i]),w[i]=1,vis[has(u[i],v[i])]=vis[has(v[i],u[i])]=0;
	for(int i=1;i<=m;i++)
	    if(w[i])vis[has(u[i],v[i])]=vis[has(v[i],u[i])]=1;
	dfs(1);
	int now=1;
	while(now<cnt)
	   {
	   	int np=now+1;
	   	while(ou[np]!=ou[now]&&np<=cnt)np++;
	   	for(int i=now+2;i<np;i++)d[++c]=3,a[c][0]=ou[now],a[c][1]=ou[i-1],a[c][2]=ou[i];
	   	now=np;
	   }
	printf("%d\n",c);
	for(int i=1;i<=c;i++)
	    {
	    	printf("%d ",d[i]);
	    	for(int j=0;j<d[i];j++)printf("%d ",a[i][j]);
	    	printf("\n");
		}
	return 0;
}
posted @ 2025-10-08 11:23  w9095  阅读(18)  评论(0)    收藏  举报