这是一道交互题

题目大意

  有一棵\(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();
}

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

  1. 【WC2018】即时战略(动态点分治,替罪羊树)

    [WC2018]即时战略(动态点分治,替罪羊树) 题面 UOJ 题解 其实这题我也不知道应该怎么确定他到底用了啥.只是想法很类似就写上了QwQ. 首先链的部分都告诉你要特殊处理那就没有办法只能特殊处理 ...

  2. uoj#349. 【WC2018】即时战略(动态点分治)

    传送门 头一次看着题解有一种咱不会\(c++\)的感觉-- 看题解吧-- //minamoto #include<bits/stdc++.h> #include "rts.h&q ...

  3. loj2341「WC2018」即时战略(随机化,LCT/动态点分治)

    loj2341「WC2018」即时战略(随机化,LCT/动态点分治) loj Luogu 题解时间 对于 $ datatype = 3 $ 的数据,explore操作次数只有 $ n+log n $ ...

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

    题目链接: [WC2018]即时战略 题目大意:给一棵结构未知的树,初始时除1号点其他点都是黑色,1号点是白色,每次你可以询问一条起点为白色终点任意的路径,交互库会自动返回给你这条路径上与起点相邻的节 ...

  5. 「WC2018即时战略」

    「WC2018即时战略」 题目描述 小 M 在玩一个即时战略 (Real Time Strategy) 游戏.不同于大多数同类游戏,这个游戏的地图是树形的.也就是说,地图可以用一个由 \(n\) 个结 ...

  6. [BZOJ3924][ZJOI2015]幻想乡战略游戏(动态点分治)

    题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...

  7. WC2018 即时战略

    交互题 一棵树,一开始只有 1 号点是已知的,其他的都是未知的,你可以调用函数 explore(x,y) ,其中 x 必须是已知的,函数会找到 x 到 y 路径上第二个点,并把它标成已知,求最小步数使 ...

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

    \(\color{#0066ff}{ 题目描述 }\) 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越 ...

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

    题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...

随机推荐

  1. vue prop 传递数据

    prop 组件实例的作用域是孤立的.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据.要让子组件使用父组件的数据,需要通过子组件的 props 选项 一个组件默认可以拥有任意数量的 p ...

  2. 二十一、当锚点遇到fixed(margin和padding)

    当锚点点击跳转的时候,如果上方有fixed,锚点跳转会默认跳转到top为0的地方,有一部分就被遮挡了 解决方法:(像素值随便给的) 给锚点跳转到的具体内容加padding-top:-50px:marg ...

  3. SQL SERVER中的两种常见死锁及解决思路

    在sql server中,死锁都与一种锁有关,那就是排它锁(x锁).由于在同一时间对同一个数据库资源只能有一个数据库进程可以拥有排它锁.因此,一旦多个进程都需要获取某个或者同一个数据库资源的排它访问权 ...

  4. 2018湘潭邀请赛C题(主席树+二分)

    题目地址:https://www.icpc.camp/contests/6CP5W4knRaIRgU 比赛的时候知道这题是用主席树+二分,可是当时没有学主席树,就连有模板都不敢套,因为代码实在是太长了 ...

  5. Codeforces Round #481 (Div. 3)Petya's Exams CodeForces - 978G

    Petya studies at university. The current academic year finishes with nn special days. Petya needs to ...

  6. How to Configure Email Notification in Jenkins

    How to Configure Email Notification in Jenkins? - The Official 360logica Bloghttps://www.360logica.c ...

  7. SpringMVC+Spring+Mybatis+AngularJS 多规格保存示例代码

    insert时拿到最新增加的id值 绑定参数 js 实体类 Service实现类 Controller

  8. Programming好文解读系列(—)——代码整洁之道

    注:初入职场,作为一个程序员,要融入项目组的编程风格,渐渐地觉得系统地研究下如何写出整洁而高效的代码还是很有必要的.与在学校时写代码的情况不同,实现某个功能是不难的,需要下功夫的地方在于如何做一些防御 ...

  9. java中去除字符串(String)中的换行字符(\r \n \t)

    例1: public class Test { public static void main(String[] args) { String s = "'sds gdasda" ...

  10. peewee 事物 回滚

    peewee 事物 回滚 #!/usr/bin/env python # coding=utf-8 from peewee import * db = MySQLDatabase(host='123. ...