【UOJ349】【WC2018】即时战略 LCT 动态点分治

这是一道交互题

题目大意

  有一棵\(n\)个点的树。最开始\(1\)号点是白的,其他点是黑的。

  每次你可以执行一个操作:\(explore(x,y)\)。要求\(x\)是一个白点。该函数会返回从\(x\)到\(y\)的路径上第二个点的坐标并把该点染白。

  要求你把所有点都染成白色。

  设操作次数为\(t\)。

  对于\(30\%\)的数据:这棵树是一条链(不保证\(1\)在链的一端),\(n=300000,t=O(n+\log n)\)

  对于另外\(70\%\)的数据:\(n=300000,t=O(n\log n)\)

题解

  数据范围告诉我们链的情况要分开做。

做法1

  有一个简单的做法:维护当前白色节点的两段,每次加入一个新的节点,如果这个节点是黑的,就先判断这个点在一号点的左边还是右边,在进行扩展。

  可以证明扩展次数是\(2\ln n\)

  记\(E(n)\)为一条长为\(n+1\)的链(有\(n\)个黑色节点),从左边开始扩展的期望次数(最左端是\(1\)号点)。
\[
\begin{align}
E(0)&=0\E(n)&=1+\frac{1}{n}\sum_{i=0}^{n-1}E(i)\n(E(n)-1)&=(n-1)(E(n-1)-1)+E(n-1)\nE(n)-n&=nE(n-1)-n+1-E(n-1)+1+E(n-1)\nE(n)-nE(n-1)&=1\E(n)-E(n-1)&=\frac{1}{n}\E(n)&=\sum_{i=1}^n\frac{1}{i}\approx\ln n
\end{align}
\]
  就是枚举\(n\)是第几个扩展的,把前面的离散化到\(1\sim i-1\)。

  因为这题的\(1\)号点不在一端,次数就要乘以\(2\)。

  期望操作次数是\(n+2\ln n\)

  但是这样很大概率拿不了满分。

  我们加入一个新的节点时,可以默认这个点是在\(1\)号点左边,扩展第一次时判断要扩展的点是否是白的。如果是白的就说明新加入的点在\(1\)号点右边。

  当然,加入新节点的第一次扩展要随机扩展左边或右边。

  这样期望扩展次数就是\(\ln n\)了,期望操作次数就是\(n+\ln n\)

做法2

  问题转化为:每次给你一个新的点,要你找出树上离这个点最近的节点。

  • 动态点分治:动态维护点分治树,在点分治树上面跳。

时间复杂度:\(O(n\log^2 n)\)

操作次数:\(O(n\log n)\)

  • LCT:每次从根往下在splay上跳,如果调到另一条链上就splay一下继续跳。

时间复杂度:\(O(n\log n)\)

操作次数:\(O(n\log n)\)

  这两种做法都可以拿到满分。

  如果你写的是动态点分治,你可能会被卡常。

  UPD:动态点分治跑的很快,LCT没写好会被卡操作常数。

代码

LCT

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<utility>
#include"rts.h"
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int n;
int b[300010];
int t;
int a[300010][2];
int f[300010];
int l[300010];
int r[300010];
int root(int x)
{
    return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
}
void mt(int x)
{
    l[x]=r[x]=x;
    if(a[x][0])
        l[x]=l[a[x][0]];
    if(a[x][1])
        r[x]=r[a[x][1]];
}
void rotate(int x)
{
    int p=f[x];
    int q=f[p];
    int ps=(x==a[p][1]);
    int qs=(p==a[q][1]);
    int c=a[x][ps^1];
    if(!root(p))
        a[q][qs]=x;
    a[x][ps^1]=p;
    a[p][ps]=c;
    if(c)
        f[c]=p;
    f[p]=x;
    f[x]=q;
    mt(p);
    mt(x);
}
void splay(int x)
{
    while(!root(x))
    {
        int p=f[x];
        if(!root(p))
        {
            int q=f[p];
            if((p==a[q][1])==(x==a[p][1]))
                rotate(p);
            else
                rotate(x);
        }
        rotate(x);
    }
}
void access(int x)
{
    int y=x,t=0;
    while(x)
    {
        splay(x);
        a[x][1]=t;
        mt(x);
        t=x;
        x=f[x];
    }
    splay(y);
}
void gao(int x)
{
    int now=1,v;
    splay(now);
    while(!b[x])
    {
        v=explore(now,x);
        if(v==r[a[now][0]])
            now=a[now][0];
        else if(v==l[a[now][1]])
            now=a[now][1];
        else if(b[v])
        {
            splay(v);
            now=v;
        }
        else
        {
            b[v]=1;
            f[v]=now;
            now=v;
        }
    }
    access(x);
}
void gao1()
{
    int i;
    for(i=2;i<=n;i++)
        if(!b[i])
            gao(i);
}
void gao2()
{
    int x1=1,x2=1;
    int i;
    b[1]=1;
    for(i=2;i<=n;i++)
    {
        int x=i;
        if(b[x])
            continue;
        int v=explore(x1,x);
        if(!b[v])
        {
            b[v]=1;
            x1=v;
            while(x1!=x)
            {
                v=explore(x1,x);
                b[v]=1;
                x1=v;
            }
        }
        else
        {
            while(x2!=x)
            {
                v=explore(x2,x);
                b[v]=1;
                x2=v;
            }
        }
    }
}
void play(int _n,int _t,int type)
{
    n=_n;
    t=_t;
    if(type==3)
        gao2();
    else
        gao1();
}

动态点分治

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<utility>
#include<vector>
#include"rts.h"
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
vector<int> g[300010];
const double alpha=0.8;
int n;
int b[300010];
int t;
int d[300010];
int f[300010];
int s[300010];
int rebuild;
void gao(int now,int x)
{
    if(now==x)
        return;
    int v=explore(now,x);
    if(b[v])
    {
        while(f[v]!=now)
            v=f[v];
        s[now]-=s[v];
        gao(v,x);
        s[now]+=s[v];
    }
    else
    {
        b[v]=1;
        g[now].push_back(v);
        g[v].push_back(now);
        d[v]=d[now]+1;
        s[v]=1;
        f[v]=now;
        gao(v,x);
        s[now]+=s[v];
    }
    if(s[v]>s[now]*alpha)
        rebuild=now;
}
void dfs(int x,int dep)
{
    b[x]=0;
    for(auto v:g[x])
        if(b[v]&&d[v]>=dep)
            dfs(v,dep);
}
int ss[300010];
void dfs1(int x,int fa)
{
    ss[x]=1;
    for(auto v:g[x])
        if(v!=fa&&!b[v])
        {
            dfs1(v,x);
            ss[x]+=ss[v];
        }
}
int rt,rtsz,num;
void dfs2(int x,int fa)
{
    int mx=0;
    for(auto v:g[x])
        if(!b[v]&&v!=fa)
        {
            dfs2(v,x);
            mx=max(mx,ss[v]);
        }
    mx=max(mx,num-ss[x]);
    if(mx<rtsz)
    {
        rtsz=mx;
        rt=x;
    }
}
int build(int x,int dep,int fa,int mi)
{
    dfs1(x,0);
    num=ss[x];
    rtsz=0x7fffffff;
    dfs2(x,0);
    x=rt;
    b[x]=1;
    d[x]=dep;
    f[x]=fa;
    s[x]=1;
    for(auto v:g[x])
        if(!b[v])
        {
            int rr=build(v,dep+1,x,mi);
            s[x]+=s[rr];
        }
    return x;
}
int cc[300010];
void gao1()
{
    int i;
    for(i=1;i<=n;i++)
        cc[i]=i;
    srand(time(0));
    random_shuffle(cc+1,cc+n+1);
    b[1]=1;
    d[1]=0;
    s[1]=1;
    f[1]=0;
    int r=1;
    for(i=1;i<=n;i++)
        if(!b[cc[i]])
//      if(!b[i])
        {
            gao(r,cc[i]);
//          gao(r,i);
            if(rebuild)
            {
                dfs(rebuild,d[rebuild]);
                int rr=build(rebuild,d[rebuild],f[rebuild],d[rebuild]);
                if(rebuild==r)
                    r=rr;
                rebuild=0;
            }
        }
}
void gao3()
{
    int x1=1,x2=1;
    int i;
    b[1]=1;
    for(i=1;i<=n;i++)
        cc[i]=i;
    srand(time(0));
    random_shuffle(cc+1,cc+n+1);
    for(i=1;i<=n;i++)
    {
        int x=cc[i];
        if(b[x])
            continue;
        int v=explore(x1,x);
        if(!b[v])
        {
            b[v]=1;
            x1=v;
            while(x1!=x)
            {
                v=explore(x1,x);
                b[v]=1;
                x1=v;
            }
        }
        else
        {
            while(x2!=x)
            {
                v=explore(x2,x);
                b[v]=1;
                x2=v;
            }
        }
    }
}
void gao2()
{
    int i,v;
    for(i=2;i<=n;i++)
    {
        int x=1;
        while((v=explore(x,i))!=i)
            x=v;
    }
}
void play(int _n,int _t,int type)
{
    n=_n;
    t=_t;
    if(type==3)
        gao3();
    else if(type==2)
        gao2();
    else
        gao1();
}

原文地址:https://www.cnblogs.com/ywwyww/p/8514552.html

时间: 2024-08-30 11:41:56

【UOJ349】【WC2018】即时战略 LCT 动态点分治的相关文章

[WC2018]即时战略——动态点分治(替罪羊式点分树)

题目链接: [WC2018]即时战略 题目大意:给一棵结构未知的树,初始时除1号点其他点都是黑色,1号点是白色,每次你可以询问一条起点为白色终点任意的路径,交互库会自动返回给你这条路径上与起点相邻的节点并且如果这个点为黑色则将它变为白色,要求在不多于给定次数的询问内使所有点变为白色. 大致思路为按一定顺序分别将n-1个点变为白点,为了防止被卡,需要对2~n的序列随机打乱再按打乱后的顺序逐个变白. 数据范围分为三种,分开讲解(假设当前要变白的点为x): 一.完全二叉树 这一部分比较简单,我们只需要

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态树分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,并

WC2018 即时战略

交互题 一棵树,一开始只有 1 号点是已知的,其他的都是未知的,你可以调用函数 explore(x,y) ,其中 x 必须是已知的,函数会找到 x 到 y 路径上第二个点,并把它标成已知,求最小步数使整棵树都已知 对于 30% 的数据,是一条链,操作次数 $O(n+logn)$ 剩下的数据,操作次数 $O(nlogn)$ $n \leq 300000$ sol: 先吐槽 loj 的交互题评测机制 把 ac 时应该输出的东西输出,然后就 a 了 不 shing 话 链的情况想了半天,题解是 xjb

BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)

这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这棵树的分治树,也就是把这棵树的重心作为根节点然后子树为他的子树的重心这样递归下去,然后每个节点存的是其子树的信息. 对于每个节点我们保存这个子树的dv的总和已经把该节点作为点的答案值 这样对于修改能在log n的时间内解决 寻找答案的时候,我们可以发现,如果现在节点的子树dv和*2大于总节点,那么向

BZOJ 3924 Zjoi2015 幻想乡战略游戏 动态树分治

题目大意:给定一棵树,每个点有一个点权,多次改变某个点的点权,多次查询带权重心到所有点的带权距离之和 此生无悔入东方,来世愿生幻想乡 首先我们考虑如何计算一个点到所有点的带权距离之和且支持修改 用动态树分治就好了嘛... 每个点记录子树中带权距离之和,以及权值之和,再在每个子树中记录一个需要减掉的版本 然后一直向上扫到根就能统计了 ↑这段话面对会写动态树分治的人,不会的先去切捉迷藏吧 然后就好搞了... 对于分治结构的每一个点,我们枚举它的出边 如果某条出边连向的点的距离之和小于当前点,那么答案

loj 2135 「ZJOI2015」幻想乡战略游戏 - 动态点分治

题目传送门 传送门 题目大意 给定一棵树,初始点权都为0,要求支持: 修改点权 询问带权重心 询问带权重心就在点分树上跑一下就行了.(枚举跳哪个子树更优) 剩下都是基础点分治. 学了一下11-dimensional的2.2k动态点分治,然后写抄出来只有1.9k??? Code /** * loj * Problem#2135 * Accepted * Time: 4492ms * Memory: 28404k */ #include <bits/stdc++.h> using namespac

[ZJOI2015]幻想乡战略游戏 - 动态点分治

先考虑无修要怎么操作. 发现在无修的情况下,我们可以用一个换根\(dp\)解决. 那么带修改的情况要怎么办呢? 每次修改重新\(dp\)一遍不就行了(雾. 好的,让我们先来敲一个\(O(N^2)\)的\(dp\). #include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll ty() { char ch = getchar(); ll x = 0, f = 1; while (ch < '0'

luogu P3345 [ZJOI2015]幻想乡战略游戏 |动态点分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来. 在游戏中,幽香可能在空地上增加或者减少一些军队.同时,幽香可以在一个空地上放置一个补给站. 如果补给站在点u上,

【WC2018】即时战略

题目描述 小M在玩一个即时战略(Real Time Strategy)游戏.不同于大多数同类游戏,这个游戏的地图是树形的. 也就是说,地图可以用一个由 n个结点,n?1条边构成的连通图来表示.这些结点被编号为 1 ~ n. 每个结点有两种可能的状态:"已知的"或"未知的".游戏开始时,只有 1号结点是已知的.在游戏的过程中,小M可以尝试探索更多的结点.具体来说,小M每次操作时需要选择一个已知的结点 x,和一个不同于 x 的任意结点 y(结点 y 可以是未知的). 然