10.21 NOTE
P4376 [USACO18OPEN] Milking Order G
tag:拓扑排序 二分答案
思路
题目显然要最大化一个 \(X\) 使得对于前 \(X\) 个约束,满足整张图联通并且是一个 \(DEG\),有一个很显然的结论是:如果一张图有一个环,那么无论加多少边还是有个环;如果一张图没有环,那么无论删多少边还是没有环。
也就是说,这道题的答案是单调的,对于一个 \(X\),若合法,则比它小的数都是合法的;若不合法,则比它大是数都是不合法的。
因此考虑二分答案。
那么现在的问题来到了怎么判这张图是否有环,有一个关于拓扑排序的小技巧:若一张图有环,那么在删边删到一定程度时这张图一定没有入度为 \(0\) 的结点了,因此在进行一遍拓扑排序之后判断是否有结点的入度仍然大于 \(0\) 即可。
根据题意,要输出拓扑序,那么直接对最终答案拓扑排序即可。
总结
拓扑排序判环
一遍拓扑排序之后判断是否还有节点的入度不为 \(0\)。
Code
#include<bits/stdc++.h>
#define Iseri namespace
#define Nina std
#define Kawaragi int
#define Momoka main
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define ll long long
#define ull unsigned long long
#define endl "\n"
#define pii pair<ll,ll>
const int maxn=100005;
const int inf=0x3f3f3f3f;
using Iseri Nina;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
//===========================================================
ll n,m,rd[maxn],u;
vector<ll>c[maxn],v[maxn];
inline void init(ll x){
for(ll i=1;i<=n;i++)rd[i]=0;
for(ll i=1;i<=n;i++)v[i].clear();
for(ll i=1;i<=x;i++){
for(ll j=0;j<c[i].size()-1;j++){
v[c[i][j]].push_back(c[i][j+1]);
rd[c[i][j+1]]++;
}
}
return;
}
inline bool check(ll x){
init(x);
queue<ll>q;
for(ll i=1;i<=n;i++){
if(rd[i]==0)q.push(i);
}
while(!q.empty()){
ll a=q.front();
q.pop();
for(auto i:v[a]){
rd[i]--;
if(rd[i]==0)q.push(i);
}
}
bool flag=false;
for(ll i=1;i<=n;i++)if(rd[i]){flag=1;break;}
if(flag)return false;
else return true;
}
inline void prt(ll x){
init(x);
priority_queue<ll,vector<ll>,greater<ll> >q;
for(ll i=1;i<=n;i++)if(rd[i]==0)q.push(i);
while(!q.empty()){
ll a=q.top();
printf("%lld ",a);
q.pop();
for(auto i:v[a]){
rd[i]--;
if(rd[i]==0)q.push(i);
}
}
return;
}
Kawaragi Momoka(){
n=read(),m=read();
for(ll i=1;i<=m;i++){
ll s=read();
for(ll j=1;j<=s;j++){
u=read();
c[i].push_back(u);
}
}
ll l=1,r=m;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
prt(r);
return 0;
}
P2607 [ZJOI2008] 骑士
思路
树形 DP,但是显然这是一个基环树上的 DP,这道题比较板,就是没有上司的舞会的形式。
总结
基环树上的 DP
先找到环,随便断开一条边,以这条边两个端点为根分别跑一次树形 DP,注意特判这条边即可。
注意:在建边的时候记得建成外向基环树,不然会因为递归深度而 MLE。
Code
#include<bits/stdc++.h>
#define Iseri namespace
#define Nina std
#define Kawaragi int
#define Momoka main
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define ll long long
#define ull unsigned long long
#define endl "\n"
#define pii pair<ll,ll>
const int maxn=1000005;
const int inf=0x3f3f3f3f;
using Iseri Nina;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
//===========================================================
ll n,w[maxn],x,vis[maxn],rt,fa[maxn],f[maxn][2],ans;
vector<ll>v[maxn];
inline void dfs(ll u){
vis[u]=1;
f[u][0]=0,f[u][1]=w[u];
for(auto i:v[u]){
if(i!=rt){
dfs(i);
f[u][0]+=max(f[i][0],f[i][1]);
f[u][1]+=f[i][0];
}else f[i][1]=-inf;
}
return;
}
inline void find(ll u){
vis[u]=1;
rt=u;
while(!vis[fa[rt]]){
rt=fa[rt];
vis[rt]=1;
}
dfs(rt);
ll tmp=max(f[rt][0],f[rt][1]);
vis[rt]=1;
rt=fa[rt];
dfs(rt);
ans+=max(tmp,max(f[rt][0],f[rt][1]));
return;
}
Kawaragi Momoka(){
n=read();
for(ll i=1;i<=n;i++){
w[i]=read(),x=read();
v[i].push_back(x);
fa[i]=x;
}
for(ll i=1;i<=n;i++)if(!vis[i])find(i);
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号