Luogu 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;
}

浙公网安备 33010602011771号