zoukankan      html  css  js  c++  java
  • LOJ2398. 「JOISC 2017 Day 3」自然公园

    交互题。有一棵树,需要通过至多(45000)次Ask操作确定这棵树的形态。

    (Ask(x,y,P))表示只通过集合(P)中的点(x)(y)是否连通。

    每个点的度数至多为(7)

    (nle 1400)


    自己想出的有77分。

    先想链咋做。定义过程(work(l,r)),表示确定夹在(l)(r)中的点。将没有确定点排成一个序列,然后分治找出任意找出一个夹在(l,r)中的点(x)(询问时取一堆点的补集表示判断这堆点是否有(l,r)间的必经点)。然后进入(work(l,x),work(x,r))递归进行。由于每个点只会被找到一次,所以次数是(O(nlg n))的。

    尝试扩展到树上。考虑扩展(0)所在的连通块。每次随意选一个块外的点(z),然后找到块中离(z)最近的点(y)。后面的问题就相当于(work(y,z))。于是每次可以在拉出一条链。拉出这条链的过程是(O(nlg n))。问题是找(y):我写的是带根点分治,次数大概是(O(nlog_{frac{3}{2}}n))

    满分做法:

    也是扩展(0)所在的连通块。找到一个块外的与块直接相连的点(x),然后搞出块内所有和它直接相连的点:以某个点为根,搞个序,满足每个点的父亲一定在这个点之前出现,dfs序和bfs序都行,然后在这个序上二分,判断是仅保留一段前缀和(x)是否连通。于是就可以找出这个序上最小的和(x)直接相连的点,把它标记删掉,原来的连通块分成几个连通块递归着重复上面的过程。

    至于如何找与块直接相连的点(x)

    可以任意找个块外点(z),用类似上面链的做法拉出一条链。具体而言,就是定义(work(z)),表示将连通块扩展到(z)。先判断块和(z)是否直接相邻。每次二分找到任意一个夹在块和(z)的点(y),然后依次做(work(y))(此时注意把(z)删除)和(work(z))

    这里的二分是:找一个最短的前缀,使得只经过这个前缀的点可以到达(z)。可以保证这个前缀尾端的那个点一定存在于(z)到这棵树的某一条路径上。这样最终一定可以拉出一条链来。


    	using namespace std;
    #include <bits/stdc++.h>
    #include "park.h"
    
    namespace Subtask_all{
    	const int N=1405;
    	void answer(int x,int y){
    //		printf("%d %d
    ",x,y);
    		Answer(min(x,y),max(x,y));
    	}
    	int ask(int x,int y,int p[]){
    		return Ask(min(x,y),max(x,y),p);
    	}
    	int p[N],p_[N];
    	int n;
    	struct EDGE{
    		int to;
    		EDGE *las;
    	} e[N*14];
    	int ne;
    	EDGE *last[N];
    	void link(int u,int v){
    		e[ne]={v,last[u]};
    		last[u]=e+ne++;
    		e[ne]={u,last[v]};
    		last[v]=e+ne++;
    	}
    	int vis[N];
    	int q[N];
    	int bz[N],BZ;
    	int era[N],BZ2;
    	void divide(int x,int z){
    		static int q[N];
    		int head=1,tail=1;
    		q[1]=x;
    		bz[x]=++BZ;
    		while (head<=tail){
    			int y=q[head++];
    			for (EDGE *ei=last[y];ei;ei=ei->las)
    				if (bz[ei->to]!=BZ && era[ei->to]!=BZ2){
    					bz[ei->to]=BZ;
    					q[++tail]=ei->to;
    				}
    		}
    		int l=1,r=tail,b=tail;
    		for (int i=1;i<=tail;++i)
    			p_[q[i]]=1;
    		int tmp=ask(x,z,p_);
    		if (tmp==0){
    			while (b)
    				p_[q[b--]]=0;
    			return;
    		}
    		while (l!=r){
    			int mid=l+r>>1;
    			for (;b<mid;++b)
    				p_[q[b+1]]=1;
    			for (;b>mid;--b)
    				p_[q[b]]=0;
    			int tmp=ask(x,z,p_);
    			if (tmp==1)
    				r=mid;
    			else
    				l=mid+1;				
    		}
    		int y=q[l];
    		while (b)
    			p_[q[b--]]=0;
    		era[y]=BZ2;
    		answer(y,z);
    		for (EDGE *ei=last[y];ei;ei=ei->las)
    			if (era[ei->to]!=BZ2)
    				divide(ei->to,z);
    		link(y,z);
    	}
    	void find(int z){
    		++BZ2;
    		p_[z]=1;
    		divide(0,z);
    		p_[z]=0;
    	}
    //	int cnt;
    	void gt(int x){
    		static int ls[N];
    		int k=0;
    		for (int i=0;i<n;++i)
    			if (!vis[i] && i!=x)
    				ls[++k]=i;
    			else
    				p_[i]=1;
    		int tmp=ask(0,x,p_);
    		if (tmp==1){
    			memset(p_,0,sizeof(int)*n);
    			find(x);
    			vis[x]=1;
    			return;
    		}
    		int b=0,l=1,r=k;
    		while (l!=r){
    			int mid=l+r>>1;
    			for (;b<mid;++b)
    				p_[ls[b+1]]=1;
    			for (;b>mid;--b)
    				p_[ls[b]]=0;
    			int tmp=ask(0,x,p_);
    //			cnt++; 
    			if (tmp==0)
    				l=mid+1;
    			else
    				r=mid;
    		}
    		for (;b;--b)
    			p_[ls[b]]=0;
    		int y=ls[l];
    		vis[x]=1;
    		gt(y);
    		vis[x]=0;
    		gt(x);
    	}
    	void work(int _n){
    		n=_n;
    		for (int i=0;i<n;++i)
    			p[i]=1;
    		vis[0]=1;
    		for (int i=0;i<n;++i)
    			q[i]=i;
    		srand(time(0)^*new(int));
    		random_shuffle(q,q+n);
    		for (int i=0;i<n;++i){
    			int x=q[i];
    			if (!vis[x])
    				gt(x);
    		}
    //		printf("%d
    ",cnt);
    	}
    }
    void Detect(int T, int n) {
    	Subtask_all::work(n);
    }
    
  • 相关阅读:
    java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)
    DexClassLoader
    防止 apk反编译 jocky-- java混淆代码 (转至:http://my.oschina.net/f839903061/blog/72554)
    Android APK反编译详解(附图) (转至 http://blog.csdn.net/ithomer/article/details/6727581)
    双卡手机怎么指定SIM卡打电话
    android设备休眠
    GSON使用笔记(3) -- 如何反序列化出List
    Notepad++ 更换主题
    如何才能成为一个成功的项目经理
    项目经理是干出来的,不是学出来的;是带出来的,不是教出来的
  • 原文地址:https://www.cnblogs.com/jz-597/p/14253506.html
Copyright © 2011-2022 走看看