[IOI 2017]simurgh

题意:有一张n个点m条边的无重边,无自环的无向联通图。
里面有一个生成树T,然后你需要找到它。
你每次可以询问一个生成树T’,交互库会返回给你T与T‘交集的大小。
满足 n ⩽ 500 , m ⩽ n ∗ ( n − 1 ) 2 n\leqslant 500,m\leqslant \frac{n*(n-1)}{2} n500,m2n(n1)
用不超过8000次询问搞定。
我们先考虑一下 n ⩽ 100 n\leqslant 100 n100时怎么办。
我们可以把整个图变成几个简单环的并集,且这些简单环接近两两不相交。
对于一个环C,我们可以用 ∣ C ∣ |C| C次询问找出其中每条边的状态。
故这种解决方法需要m次。
不妨再想一种特殊情况,即给出的图是一张完全图的时候。
分两步走:
第一步: 求出一颗生成树S,并求出其中每条边的状态
在完全图中,每条边都位于一个长度为3的环上。
所以这里每条边的状态可以用3次询问出来。
第二步: 逐步加边,扩展成T
求出S上的边的状态有一个好处,就是对于图上的一个森林M,你可以求出M中有多少条边是在T上的。
做法如下:类似kruskal一样,将S的边往M加,即能联通就加。
这样形成了一个生成树N,询问N并去除掉S的边的影响即可得到M中有多少条边是在T上的。
有了这样一种神奇操作后,我们考虑逐步加边。
我们可以动态更新每个点的度数。
每个点当前的度数即为未加的边中以它为端点的条数。
一开始,即加了0条边的时候,每个点的度数可以通过询问原图中以它为端点的边构成的森林得出。
加入一条边(u,v)时, d e g r e e u − − , d e g r e e v − − degree_u--,degree_v-- degreeu,degreev即可
每次我们拿出当前一个度数为大于0的点,通过二分寻找到一条边即可。
如此循环n-1次。
在一般图上,我们也走上面两步,但第一步的实现方法在一般图上不太适用。
必须改进。采用的是找环,但这个找环跟上面那个找环不同。
割边必为树边。
S中的非割边一定会位于至少一个环上。
对于不在S的边(u,v),(u,v)和u,v在S上的简单路径会形成一个环。
如果能求出所有这种环上每条边的状态,那么S上每条边的状态就可以求出。
好!
逐步加入这种环,假设当前环是C。
假设C中S的边中未求出的边有x条。
那么当x=0,不管。
当x=C中S的边,便历整个环,需要x+1次询问。
当x 因为每次考虑一个环时x>0,所以这一部分最多询问2n-2次。
第二部分最多询问 n + n l o g 2 n n+nlog_2n n+nlog2n次。
故总询问次数 3 n + n l o g 2 n 3n+nlog_2n 3n+nlog2n,时间复杂度 O ( n m ) O(nm) O(nm)
可以在IOI官网上下载数据。

#include
#include
#include
#include
#include
#include "simurgh.h"
using namespace std;
int n;
#define V 505
#define E 130010
vector edge1,edge2;
int bel[V],fa[V],depth[V];
int head[V],v[V<<1],nxt[V<<1],tot=0;
int seq[E],cnt=0;
vector tree;
int rel[V][V];
bool vis[V];
bool mark[E],ans[E];
vector answer;
inline void add_edge(int s,int e){
	tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;
	tot++;v[tot]=s;nxt[tot]=head[e];head[e]=tot;
}
int getroot(int x){
	if(bel[x]!=x)bel[x]=getroot(bel[x]);
	return bel[x];
}

void dfs(int u,int f){
	fa[u]=f;
	for(int i=head[u];i;i=nxt[i])
		if(v[i]^f){
			depth[v[i]]=depth[u]+1;
			dfs(v[i],u);
		}
}

vector vec;
int solve1(){
	for(register int i=0;idepth[b])a=fa[a];
	while(depth[a]maxv){
				maxv=res[i];
				secmaxv=maxv;
			}else if(res[i]secmaxv)secmaxv=res[i];
		}
		if(secmaxv==-1){
			for(register int i=1;i find_roads(int n,vector e1,vector e2){
	init(n);
	memset(rel,-1,sizeof(rel));
	for(register int i=0;i>1;
			if(Judge(mid))r=mid;
			else l=mid+1;
		}
		degree[edge1[pass[l]]]--;
		degree[edge2[pass[l]]]--;
		answer.push_back(pass[l]);
		mark[pass[l]]=true;
		ans[pass[l]]=true;
	}
	return answer;
}/*
g++ -std=c++11 -Wall -O2 -o simurgh grader.cpp simurgh.cpp
*/

你可能感兴趣的:(日常习题)