zoukankan      html  css  js  c++  java
  • CF1361E James and the Chase

    一、题目

    点此看题

    二、解法

    首先考虑如何判断一个点合法,以这个点为根建出 ( t dfs) 树,当且仅当这棵树中只存在树边和返祖边时合法,那么判定单点合法

    貌似没有什么好的思路,考虑有解合法点数至少有 (frac{n}{5}) 个,可以利用这个性质找一个合法的点,如果我们随机 (k) 次,那么错误的概率是 ((frac{4}{5})^k),取 (k=100) 就基本上稳对了,如果找不到就判定为无解。

    设找到的合法点为 (rt),那么以 (rt) 建出一棵 ( t dfs) 树,然后我们考虑一个结点 (u) 怎么样才能合法。

    找必要条件,由于只有返祖边,所以 (u) 只能通过返祖边走出这个子树,再考虑子树内走出子树的返祖边只能有一个。因为如果有两个那么到 (u) 的父亲就有两种方案,如果没有那么走不出子树。

    那么返祖边连向的点 (v) 有什么性质呢?很显然他必须要是好的,因为要通过它走到子树外的点,而如果从 (v) 开始走到某个点有多条简单路径那么 (u) 走到它也有多条简单路径,因为只能走简单路径所以不能通过 (v) 走到子树内。

    那么我们启发式合并求出子树内的返祖边即可,可以用 ( t set) 之类的数据结构维护,时间复杂度 (O(nlog^2 n))

    三、总结

    图论问题一定要主动放在 ( t dfs) 树上去考虑。

    本题的难点是对于 (20\%) 的思考,我的理解是这是一个关键的阈值,如果达到了 (20\%) 就能做某事,如果达不到 (20\%) 就可能做不成某事(不是一定做不成),那么思考这件事是什么即可。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <ctime>
    #include <set>
    using namespace std;
    const int M = 100005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,m,rt,ans,Ind,id[M],fa[M],up[M],ok[M],in[M],out[M];
    vector<int> g[M];multiset<int> s[M];
    void dfs1(int u)
    {
    	in[u]=++Ind;
    	for(auto v:g[u])
    		if(!in[v]) fa[v]=u,dfs1(v);
    	out[u]=Ind;
    }
    int check(int x)
    {
    	//printf("%d
    ",x);
    	for(int i=1;i<=n;i++)
    		in[i]=out[i]=fa[i]=0;
    	Ind=0;dfs1(x);
    	for(int u=1;u<=n;u++)
    		for(auto v:g[u]) if(fa[v]^u)
    			if(in[v]>in[u] || out[v]<out[u])
    				return 0;
    	return 1;
    }
    void dfs2(int u)
    {
    	for(auto v:g[u])
    	{
    		if(in[v]<=in[u])//u's ancestor
    			s[u].insert(v);
    		else
    		{
    			dfs2(v);
    			if(s[u].size()<s[v].size()) swap(s[u],s[v]);
    			for(auto x:s[v]) s[u].insert(x);
    		}
    	}
    	s[u].erase(u);
    	if(s[u].size()==1) up[u]=*s[u].begin();
    }
    void dfs3(int u)
    {
    	ok[u]|=ok[up[u]];
    	if(ok[u]) ans++;
    	for(auto v:g[u])
    		if(in[v]>in[u]) dfs3(v);
    }
    void work()
    {
    	n=read();m=read();rt=ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		g[i].clear();s[i].clear();
    		id[i]=i;ok[i]=up[i]=0;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read();
    		g[u].push_back(v);
    	}
    	random_shuffle(id+1,id+1+n);
    	for(int i=1;i<=min(n,100);i++)
    		if(check(id[i]))
    		{
    			rt=id[i];
    			break;
    		}
    	if(!rt) {puts("-1");return ;}
    	dfs2(rt);
    	ok[rt]=1;
    	dfs3(rt);
    	if(ans*5<n) {puts("-1");return ;}
    	for(int i=1;i<=n;i++)
    		if(ok[i]) printf("%d ",i);
    	puts("");
    }
    signed main()
    {
    	T=read();srand(time(0));
    	while(T--) work();
    }
    
  • 相关阅读:
    第六章实验报告
    第三次实验报告
    第五章 循环结构课后反思
    第二次实验报告
    第一次实验报告
    第一次课后作业
    第五章 循环结构
    第九章实验报告
    第八章实验报告
    第七章 数组实验
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15158524.html
Copyright © 2011-2022 走看看