【2021.5.8 NOI模拟】贪心
交互题。
一棵有根树(不知道根)。保证不存在点恰好有一个儿子。需要确定树的形态。
每次可以询问一个长度任意的序列,表示将这些序列中的点顺序加入,记个计数器\(cnt\),如果之前没有假如和它互为祖先后代关系的点,则\(cnt+1\)。返回\(cnt\)。
\(n\le 3000\)
不超过\(45000\)次询问。
给节点标号,满足:1. 标号相同的点在同一条祖先后代链上。2. 任意点的祖先的标号都小于等于这个点。
用阳间的说法来说:维护个链剖分,同一条链上的标号相同,上面的链标号小于下面的链。
增量造,假设之前加入的点已经标了\(1\dots k\),标号为\(i\)的集合为\(S_i\),当前加入点\(x\)。
先\(ask(x,S_k,\dots,S_1)\),如果查出\(k+1\),则另开标号。(因为\(k\dots 1\),相当于是从下往上扫链,于是这些链不会互相影响)。
否则,二分出最小的\(c\),\(ask(x,S_c,\dots,S_1)=c\),于是找到了\(x\)所在的链。(注意不要找\(S_n,\dots,S_c\),因为后面的可能是\(x\)的后代,也能和\(x\)冲突。只有从前往后第一个与\(x\)冲突的才是\(x\)所在的链。)
上面这部分大概用了\(k+(n-k)\lg n\)次。
最终我们得到了所有点的标号。按照标号顺序从大到小还原:之前有标号更大的,但是链顶没有确定父亲的链,将这些链顶和当前标号下每个点查询,通过结果,就可以知道多少个链顶是它的后代。算出之后排序,能知道链的顺序,然后从深到浅,分治找出每个点的儿子。
下面这部分大概用了\(n+k\lg n\)次。
#include "greedy.h"
#include <bits/stdc++.h>
using namespace std;
namespace Subtask5{
const int N=3005;
int vec[N],nv;
vector<int> q[N];
void ins(vector<int> &p){
for (int i=0;i<p.size();++i)
vec[nv++]=p[i];
}
int n,k;
bool bz[N];
int w[N];
bool cmpw(int a,int b){return w[a]<w[b];}
int p[N],cnt,tot,del;
int fa[N];
void lk(int x,int l,int r,int c){
if (l==r){
bz[p[l]]=1;
fa[q[p[l]][q[p[l]].size()-1]]=x;
++del;
return;
}
int mid=l+r>>1;
nv=0,vec[nv++]=x;
for (int i=l;i<=mid;++i)
ins(q[p[i]]);
int tmp=(mid-l+1)-(ask(nv,vec)-1);
if (tmp) lk(x,l,mid,tmp);
if (c-tmp) lk(x,mid+1,r,c-tmp);
}
void work(int _n){
n=_n;
for (int x=1;x<=n;++x){
nv=0,vec[nv++]=x;
for (int i=k;i>=1;--i)
ins(q[i]);
int tmp=ask(nv,vec);
if (tmp==k+1)
q[++k].push_back(x);
else{
int l=1,r=k-1,y=k;
while (l<=r){
int mid=l+r>>1;
nv=0,vec[nv++]=x;
for (int i=mid;i>=1;--i)
ins(q[i]);
tmp=ask(nv,vec);
if (tmp==mid+1)
l=mid+1;
else
r=(y=mid)-1;
}
q[y].push_back(x);
}
}
for (int i=k;i>=1;--i){
tot=0;
nv=1;
for (int j=k;j>i;--j)
if (!bz[j])
ins(q[j]),++tot;
for (int t=0;t<q[i].size();++t){
int x=q[i][t];
vec[0]=x;
int tmp=ask(nv,vec);
w[x]=tot-(tmp-1);
}
sort(q[i].begin(),q[i].end(),cmpw);
del=0;
for (int t=0;t<q[i].size();++t){
int x=q[i][t];
if (t+1<q[i].size())
fa[x]=q[i][t+1];
if (t){
cnt=0;
for (int j=k;j>i;--j)
if (!bz[j])
p[++cnt]=j;
lk(x,1,cnt,w[x]-del);
}
}
}
answer(fa+1);
}
}
void solve(int n,int ty){
Subtask5::work(n);
}