zoukankan      html  css  js  c++  java
  • loj2341. 「WC2018」即时战略

    题面

    题解:首先发现对于datatype=3的点,允许的询问次数是(O(n+log_n))级别的。那么先考虑树是一条链的情况。考虑当前我们已经找到了这个链的中间一段,我们称其左端点为(l),右端点为(r),那么此时有意义的询问显然是((l/r,x))(x)是一个未被探索到的点。我们称一次询问“失败”,当且仅当询问的结果是一个已知的节点。如果一次询问(假设是((l,x)))成功了,那么从(l)一直往左拓展,我们就能找到(l)(x)这条链上的所有点。否则(x)一定在(r)那边,我们向右拓展,也能找到一条链上的所有点。考虑毒瘤出题人可能会可以造数据卡你,我们把([2,n])这个序列random_shuffle一下,这样,如果当前还有(m)个点没被探索到,一次探索过程期望能找到(frac{m}{2})个节点,所以只会有logn次探索过程。由于每次探索过程只有在第一次探索时可能会失败,所以失败的期望次数是(O(logn))的,可以通过。

    再考虑datatype=1,2的情况,此时的操作次数限制大概是(O(nlogn))。也就是说,每次需要只用log次询问就找到一个未被访问的点。先考虑一个(O(n^2))的暴力做法:每次都从1号节点开始探索。对于datatype=2的情况,这样做是正确的,因为这棵有根树的最大深度是(O(logn))级别的。那么考虑对于普通情况,如何将比较深的树转化为一棵比较“均衡”的树。

    想到做动态点分治时,点分树的深度是(O(logn))的,那么考虑对于已经通过探索得到的边,建出它的点分树。这样,每次只需要最多(O(logn))次询问,就能找到至少一个未被访问的点。

    但是考虑将新找到的点加入后,点分树的平衡性可能会被破坏,但我们又不可能每次都重构点分树,那么考虑替罪羊树的思想,定义一个alpha值,对于每个分治中心(u),如果在点分树上存在一个儿子的(siz>siz_u imes alpha),我们就重构这个分治中心的点分树(只修改点分树上的一个子树)。那么我们每次找到一些新节点并加入点分树后,跳一下father,找到点分树上深度最小的需要重构的点(u),进行重构即可。

    当然用lct也可以维护这个东西。

    操作次数和时间复杂度都是(O(nlogn))的,至于重构的复杂度证明,可以搜一下有关替罪羊树的博客。

    代码:(wc的所有测试点都过了,log上死活过不去extra test,无限d造数据人

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<cassert>
    #include "rts.h"
    using namespace std;
    #define F(x,y,z) for(int x=y;x<=z;x++)
    #define FOR(x,y,z) for(int x=y;x>=z;x--)
    #define I void
    #define IN int
    #define C(x,y) memset(x,y,sizeof(x))
    #define STS system("pause")
    typedef double db;
    const int INF=1e9+7;
    //IN explore(int,int);
    map<int,int>mp[303000];
    int N,maxi,Son,lim,cano[300300],num,siz[303000],mx[303000],head[303000],v[303000],vis[303000],tot,sz[303000],fa[303000],xu[303000];
    int root,rot;
    I solve3(int n){
    	F(i,2,n)xu[i]=i;sort(xu+2,xu+1+n);int L,R;L=R=1;
    	v[1]=1;
    	F(cur,2,n){
    		if(v[xu[cur]])continue;
    		int i=xu[cur],p=explore(L,i),now;
    		if(!v[p]){
    			now=p;
    			while(1){
    				v[now]=1;if(now==i)break;
    				now=explore(now,i);
    			}
    			L=i;
    		}
    		else{
    			now=explore(R,i);
    			while(1){
    				v[now]=1;if(now==i)break;
    				now=explore(now,i);
    			}
    			R=i;
    		}
    	}
    }
    struct E{
    	int to,nt;
    }e[606000];
    #define T e[k].to
    I add(int x,int y){e[++tot].to=y;e[tot].nt=head[x];head[x]=tot;}
    I findroot(int &roo,int x,int fat){
    	siz[x]=1;mx[x]=0;
    	for(int k=head[x];k!=-1;k=e[k].nt){
    		if(vis[T]||T==fat)continue;
    		findroot(roo,T,x);
    		siz[x]+=siz[T];mx[x]=max(mx[x],siz[T]);
    	}
    	mx[x]=max(mx[x],N-siz[x]);
    	if(mx[x]<maxi)roo=x,maxi=mx[x];
    }
    I getroot(int &roo,int pos,int sum){
    	maxi=INF;N=sum;findroot(roo,pos,0);
    }
    I D_1(int x,int fat){
    	siz[x]=1;
    	for(int k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat||vis[T])continue;
    		D_1(T,x);siz[x]+=siz[T];
    	}
    }
    I divided(int x,int fat){
    //	cerr<<"#"<<x<<" "<<fat<<endl;
    	fa[x]=fat;mp[x].clear();
    	D_1(x,fat);vis[x]=1;sz[x]=siz[x];
    	for(int k=head[x];k!=-1;k=e[k].nt){
    		if(T==fat||vis[T])continue;
    		int rt;getroot(rt,T,siz[T]);
    		mp[x][T]=rt;divided(rt,x);
    	}
    }
    I D_2(int x,int fat){
    	vis[x]=0;num++;
    	for(int k=head[x];k!=-1;k=e[k].nt){
    		if(cano[T]){if(T==lim)Son=x;continue;}if(T==fat)continue;
    		D_2(T,x);
    	}
    }
    I rebuild(int n){
    	int pos=lim=fa[n];while(pos)cano[pos]=1,pos=fa[pos];
    //	cerr<<"!"<<n<<endl;
    	num=0;D_2(n,0);
    	int rt;getroot(rt,n,num);pos=fa[n];while(pos)cano[pos]=0,pos=fa[pos];
    	if(n==root){root=rt;divided(root,0);}
    	else{
    		mp[lim][Son]=rt;assert(rt);divided(rt,lim);
    	}
    }
    IN ck(int x,int y){
    	db w=x*0.6;if(w<y*1.0)return 1;
    	return 0;
    }/**/
    void play(int n,int _T,int type){
    	/**/if(type==3)return solve3(n),void();
    	F(i,2,n)xu[i]=i;random_shuffle(xu+2,xu+n+1);
    	C(head,-1);tot=-1;
    	root=v[1]=sz[1]=vis[1]=1;
    	F(cur,2,n){
    		if(v[xu[cur]])continue;
    //		cerr<<"@"<<xu[cur]<<endl;
    		int i=xu[cur],p=root,d;
    		while(1){
    //			cerr<<"@";
    //			assert(p);
    			d=explore(p,i);
    			if(!v[d])break;
    			p=mp[p][d];
    		}
    		int cnt=1,_son=d,now=d,sn=0;v[d]=1;add(p,d);add(d,p);
    		while(now^i){
    //			cerr<<"#";
    			d=explore(now,i);cnt++;N++;
    			add(now,d);add(d,now);v[d]=1;
    			now=d;
    		}
    		getroot(rot,now,cnt);mp[p][_son]=rot;divided(rot,p);
    		while(p^root){
    //			cout<<"$";
    			sz[p]+=cnt;if(ck(sz[fa[p]]+cnt,sz[p]))sn=fa[p];
    			p=fa[p];
    		}
    		sz[root]+=cnt;
    		if(sn)rebuild(sn);
    //		F(j,1,n)if(v[j])cerr<<"%"<<j<<" "<<fa[j]<<endl;
    	}
    }
    //g++ grader.cpp rts.cpp -o rts -O2
    
  • 相关阅读:
    RIP2与OSPFv2 动态路由协议区别
    Linux平台下SSD的TRIM指令的最佳使用方式(不区别对待NVMe)
    MLNX网卡驱动安装
    字符串/字符数组读入(char/string)
    【NOIP2016模拟3】图书列表
    活动选择-贪心
    数列极差问题-STL优先队列-贪心
    货物搬运-贪心
    【NOIP 2002提高】均分纸牌-贪心
    【HAOI2008】糖果传递-贪心
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/13348086.html
Copyright © 2011-2022 走看看