专栏文章
题解:CF1906I Contingency Plan 2
CF1906I题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @miqdbboa
- 此快照首次捕获于
- 2025/12/04 02:56 3 个月前
- 此快照最后确认于
- 2025/12/04 02:56 3 个月前
Contingency Plan 2
题解区全都是暴力跑匈牙利/Dinic,完全没利用到本题性质啊(怎么官解也是 flow)。来篇 的树形 dp。
结论很简单:合法当且仅当最终拓扑序中相邻两点均有连边(显然相邻的两点不可能间接有边)。因为如果存在相邻两点没有连边则可以交换他们。
于是我们就要尽量利用上本来的边,即求最小链覆盖。本题是有向树,因此可以树形 dp:设 表示 的父边是/否被利用上时 子树内的最小链覆盖。转移只需要考虑 这个点的覆盖状态即可。
dp 完后得到方案,对链进行缩点,按此时的拓扑序依次连接即可。
代码:
CPP//Date: 2025-01-31 11:01:59
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
#define P emplace_back
#define CLEAR(a,v) memset((a),(v),sizeof((a)))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
char buf[1<<20],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
inline int rd() {
int s=0,m=0;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
}
bool MBE;
namespace SOLVER {
#define FORGRAPH for(int i=h[u],v;v=e[i].v;i=e[i].nxt)
int n,fe[100005],f[100005][2],fa[100005],S[100005],T[100005],h[100005],cnt;
int in[100005],out[100005],deg[100005];vector<int> g[100005];queue<int> q;
struct Edge{int v,nxt;} e[200005];inline void add(int u,int v) {e[++cnt]={v,h[u]},h[u]=cnt;}
void dfs(int u,int p) {
int sum=0,Mn[2]={0x3f3f3f3f,0x3f3f3f3f};
FORGRAPH if(v!=p) fe[v]=i&1,dfs(v,u),sum+=f[v][0],Mn[fe[v]]=min(Mn[fe[v]],f[v][1]-f[v][0]);
if(!sum) {f[u][0]=f[u][1]=1;return;}
f[u][0]=min({sum+Mn[0],sum+Mn[1],sum+Mn[0]+Mn[1]-1}),f[u][1]=min(sum+1,sum+Mn[fe[u]]);
}
void print(int u,int p,int k) {
int sum=0,Mn[2]={0x3f3f3f3f,0x3f3f3f3f},f0=-1,f1=-1;if(k) fa[u]=fa[p];
FORGRAPH if(v!=p) sum+=f[v][0],Mn[fe[v]]=min(Mn[fe[v]],f[v][1]-f[v][0]);if(!sum) return;
if(!k) {
if(f[u][0]==sum+Mn[0]) f0=1,f1=0;
if(f[u][0]==sum+Mn[1]) f0=0,f1=1;
if(f[u][0]==sum+Mn[0]+Mn[1]-1) f0=1,f1=1;
}
else {
if(f[u][1]==sum+1) f0=0,f1=0;
if(f[u][1]==sum+Mn[fe[u]]) f0=(fe[u]==0),f1=(fe[u]==1);
}
FORGRAPH if(v!=p) {
if(fe[v]==0&&f0&&f[v][1]-f[v][0]==Mn[0]) {print(v,u,1),f0=0;continue;}
if(fe[v]==1&&f1&&f[v][1]-f[v][0]==Mn[1]) {print(v,u,1),f1=0;continue;}
print(v,u,0);
}
}
void MAIN() {
cin>>n;rep(i,1,n-1) {int u=rd(),v=rd();add(u,v),add(v,u);}
rep(i,1,n) fa[i]=i;dfs(1,0);print(1,0,0);cout<<f[1][0]-1<<endl;
for(int i=1,u,v;u=e[i+1].v,v=e[i].v;i+=2) if(fa[u]==fa[v]) out[u]++,in[v]++;else deg[fa[v]]++,g[fa[u]].P(fa[v]);
rep(i,1,n) {if(!in[i]) S[fa[i]]=i;if(!out[i]) T[fa[i]]=i;if(fa[i]==i&&!deg[i]) q.push(i);}
int now=0;while(q.size()) {
int u=q.front();q.pop();if(now) printf("%d %d\n",now,S[u]);now=T[u];
for(int v:g[u]) if(--deg[v]==0) q.push(v);
}
}
}
bool MED;
signed main() {
for(int tt=1;tt;tt--) SOLVER::MAIN();
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...