CF1679D
solution:二分&记忆化搜索
对于 \(ans\) ,容易想到随着 \(ans\) 的增大这张图能走的边就会更多,这是个单调增。
所以考虑二分。
对于 \(check\) 函数,先将可以走的边建成图,在对于图上每个点跑记忆化搜索,这样可以保证 \(check\) 时线性的,定义 \(f_i\) 为走到 \(i\) 时还能走多少个点。
显然他的儿子能到达的最远距离+到他这的距离 \(\geq\) k时就成立。
当第一遍搜时,就已经达到k,显然也是可行的。
对于环,我们考虑将搜到的点打标记,回溯的时候撤回标记,在搜的过程中如果搜到打了标记的,那就形成环。
总复杂度: \(O((n+m)~log~n)\) 。
code
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define re register
#define int long long
#define fep(i,l,r) for(re int i=l;i<=r;++i)
#define For(i,u) for(re int i=head[u];i;i=e[i].nxt)
#define feb(i,r,l) for(re int i=r;i>=l;--i)
using namespace std;
const int N = 2e6+5;
int n,m,k,head[N],tot,b[N],a[N],ans=-1,u[N],v[N],f[N]; bool vis[N];
struct Edge
{
int v,nxt;
}e[N];
inline int read() { int s=0,w=1; char ch=getchar(); while(!(ch<='9'&&ch>='0')) {if(ch=='-') w=-1; ch=getchar();} while(ch<='9'&&ch>='0') {s=(s<<1)+(s<<3)+ch-'0'; ch=getchar();} return s*w; }
inline void write(int x) { if(x>=10) write(x/10); putchar(x%10+'0'); }
inline void print(int x) { if(x<0) putchar('-'),x=~(x-1); write(x); }
inline bool cmp(int d1,int d2)
{
return d1<d2;
}
inline void add(int u,int v)
{
e[++tot]=(Edge){v,head[u]}; head[u]=tot;
}
inline bool dfs(int u,int p)
{
if(p==k) return true;
vis[u]=true;
For(i,u)
{
if(vis[e[i].v]) return true;//形成了环
if(f[e[i].v])//已经深搜过了
{
if(p+f[e[i].v]+1>=k) return true;//如果他的儿子能到达的最远距离+到他这的距离 >= k
}
else
{
bool flag=dfs(e[i].v,p+1);
if(flag) return true;//成功了就返回
}
f[u]=max(f[u],f[e[i].v]+1);//更新他能到达的最远距离
}
vis[u]=false; return false;
}
inline bool check(int res)
{
fep(i,1,n) head[i]=0,vis[i]=false,f[i]=0;
while(tot) e[tot--]={0,0};
fep(i,1,m)
{
if(res>=a[u[i]]&&res>=a[v[i]]) add(u[i],v[i]);
}
fep(i,1,n) if(a[i]<=res) if(dfs(i,1)) return true;
return false;
}
signed main()
{
n=read(),m=read(),k=read();
fep(i,1,n) b[i]=a[i]=read();
fep(i,1,m) u[i]=read(),v[i]=read();
sort(b+1,b+1+n,cmp);
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(b[mid])) ans=b[mid],r=mid-1;
else l=mid+1;
}
print(ans);
return 0;
}

浙公网安备 33010602011771号