[WC2018]即时战略

3 篇文章 0 订阅
1 篇文章 0 订阅

继紫荆花之恋后第二道蒟蒻所知道的会动的树的点分治

题意:给你一颗未知的树,你只知道1,你可以询问explore(u,v),得到(u,v)路径上的第一个点,请你在有限的询问次数内确定这一棵树

首先随机化一下访问次数,这样就不会被奇奇怪怪地卡掉了(虽然出题人也是随的数据)

①:链,询问次数限制n+logn,记录链的两端,一端不对跳到另一端,期望出错次数logn

②:树,询问次数限制nlogn

我们可以每次跳到explore得到的点所属的下一层重心,这样每次少去一半的点,就可以保证询问次数是nlogn了

所以现在考虑的就是怎么维护动态的重心,所以大概按照紫荆花之恋的做法就 ok o k

但其实有两种写法,

第一种

就是每次找到一个新的点就看看树是否平衡(这样就和紫荆花之恋一模一样了)

第二种

就是把那条未访问的链一次性走到底,然后再去重构点分树

这里说一下第二种做法

后面一直在考虑,怎么维护这个点所属的下一层重心??

参考了别人的代码后发现可以用 map m a p 存一下 G[u][v] G [ u ] [ v ] ,表示 u u 所属的重心走到v后所属的下一层重心

然后我想到重构的时候怎么去dfs清空这棵树?

然后发现可以用 auto. a u t o . ….. c++11 c + + 11 真是神奇,这样的话就是最后拉一条链出来后再chk就好了

然后实际上第二种要快得多(其实想想也知道)

#include<bits/stdc++.h>
#include"rts.h"
#define fp(i,a,b) for(int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(int i=a,I=b-1;i>I;--i)
#define go(i,u) for(int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define get explore
#define pb push_back
using namespace std;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
const int N=3e5+5;
const double alpha=0.777;
typedef int arr[N];
struct eg{int nx,to;}e[N<<1];
int n,ce,rt,Rt,Sum;arr sz,fx,Fa,Fr,fi,Dsz;bool vis[N];
vector<int>id,New,S;
map<int,int>G[N];
inline void add(int u,int v){e[++ce]=eg{fi[u],v},fi[u]=ce;}
inline void adc(int fa,int rt,int in){Fa[rt]=fa,G[fa][in]=rt,Fr[rt]=in;}
void gr(int u,int fa){
    fx[u]=0,sz[u]=1;
    go(i,u)if(!vis[v]&&v^fa)gr(v,u),sz[u]+=sz[v],cmax(fx[u],sz[v]);
    cmax(fx[u],Sum-sz[u]);if(fx[u]<fx[rt])rt=u;
}
void sol(int u){
    Dsz[u]=vis[u]=1;int s;
    go(i,u)if(!vis[v])Sum=sz[v],gr(v,rt=0),sol(s=rt),adc(Fa[s]=u,s,v),Dsz[u]+=Dsz[s];
}
void clr(int u){vis[u]=0;for(auto i:G[u])clr(i.second);G[u].clear();}
inline void Re(int u){
    clr(u);Sum=Dsz[u];gr(u,rt=0);
    if(u==Rt)Fa[Rt=rt]=0;else adc(Fa[u],rt,Fr[u]);sol(rt);
}
inline void find(int x){
    int u=Rt,v;New.clear(),S.clear();
    while(u^x){
        v=get(u,x);
        if(!vis[v])add(u,v),add(v,u),adc(u,v,v),vis[v]=1,New.pb(v);
        S.pb(u=G[u][v]);
    }
    for(int i:S)Dsz[Fa[i]]-=Dsz[i];for(int i:New)Dsz[i]=1;
    for_each(S.rbegin(),S.rend(),[](int i){Dsz[Fa[i]]+=Dsz[i];});
    for(int i:S)if(Dsz[i]>Dsz[Fa[i]]*alpha)return Re(Fa[i]);
}
inline void chain(){
    int L=1,R=1;
    for(int i:id)if(!vis[i]){
        int x=get(L,i);
        if(vis[x])x=R,R=i;
        else L=i,vis[x]=1;
        while(x!=i)vis[x=get(x,i)]=1;
    }
}
void play(int _,int,int ty){
    n=_;Rt=vis[1]=1;fx[0]=N;fp(i,2,n)id.pb(i);
    srand(time(0)),random_shuffle(id.begin(),id.end());
    if(ty==3)return chain();
    for(int i:id)if(!vis[i])find(i);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值