逃离藏宝洞
参考 \(\rm grader\) 的实现可以找到一个做法:随便走一条边,判定高度,如果走错了再走回来并把这条边的标号 \(\rm ban\) 掉
这个做法需要 \(n\) 次深度询问,注意到题目中给定的交互次数限制并不严格,那么可以直接随机向上跳一段再进行询问深度,根据新的深度和原来的深度可以得到向上跳的步数
注意如果跳的路径是折线,那么这个点其实有两条出边被 \(\rm ban\) 了,可以再向上跳一步,同时随机出边时相邻两个向上跳的边不能相同
随机跳的步数设置为 \(20\) 时,在实际测试下每次向上跳的步数期望为 \(2\),可以通过
期望向上跳的步数可以使用 \(\lim\limits_{n\to +\infty} \sum_{i=0}^n\frac{1}{2^i}=2\) 来进行简单分析
Code Display
#include"escape.h"
const int N=1010;
int v[N];
inline void get_random(int len,int lst){
for(int i=1;i<=len;++i){
int x=random(0,2);
while(lst==x) x=random(0,2);
lst=v[i]=x;
}
}
void escape(int lm, int lq){
int lst=-1;
int dep=abs(query());
while(dep){
int len=min(lm==3002?1:30,dep);
get_random(len,lst);
for(int i=1;i<=len;++i) move(v[i]);
int cur=abs(query());
int up=(len+dep-cur)/2;
for(int i=len;i>up;--i) move(v[i]);
dep-=up;
if(up==len) lst=v[up];
else{
if(~lst){
if(up) lst=v[up];
rep(i,0,2) if(lst!=i&&v[up+1]!=i){move(lst=i); break;}
dep--;
}else lst=v[up+1];
}
}
}
序列划分
设 \(f_i\) 表示前 \(i\) 个元素进行划分得到的 \(f\) 函数之和,转移枚举最后一段划分在哪里
根据 \(a_i\le 10\) 觉得部分可以发现每次转移将 \(\rm mex\) 相同的一起做是减少冗余的一个方式
但是显然可以再给力一些,使用线段树容易在挪动右端点时维护每个左端点为起点时这段的 \(\rm mex\) ,把 \(f\) 值挂到叶子上那么就是区间求和,也能应付 \(\rm mex\) 的修改
被卡常可以将单点修改写成 \(\rm zkw\) 的形式
Code Display
const int N=1e6+10;
int a[N],n,Q,app[N];
struct node{
int mex,l,r;
bool operator <(const node &a)const{return mex<a.mex;}
};
set<node>now;
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
struct segment_tree{
int Mn[N<<2];
inline void push_up(int p){Mn[p]=min(Mn[ls],Mn[rs]);}
inline void modify(int pos,int v,int p=1,int l=0,int r=n){
if(l==r) return Mn[p]=v,void();
int mid=(l+r)>>1;
if(pos<=mid) modify(pos,v,lson);
else modify(pos,v,rson);
return push_up(p);
}
inline int erf(int tar,int v,int p=1,int l=0,int r=n){
if(Mn[p]>tar) return -1;
if(l==r) return l; int mid=(l+r)>>1;
if(v<=l){
if(Mn[ls]<=tar) return erf(tar,v,lson);
return erf(tar,v,rson);
}
if(v>mid) return erf(tar,v,rson);
int res=erf(tar,v,lson);
if(~res) return res; return erf(tar,v,rson);
} //larger than v,backer than tar
}lst;
struct Segment_Tree{
int sum[N<<2],val[N<<2],id[N],cov[N<<2];
inline void build(int p,int l,int r){
cov[p]=-1;
if(l==r) return id[l]=p,void(); int mid=(l+r)>>1;
build(lson); build(rson);
return ;
}
inline void push_cov(int p,int v){
sum[p]=mul(val[p],cov[p]=v);
return ;
}
inline void push_down(int p){
if(~cov[p]){
push_cov(ls,cov[p]);
push_cov(rs,cov[p]);
cov[p]=-1;
} return ;
}
inline void push_up(int p){
val[p]=val[ls]+val[rs];
sum[p]=sum[ls]+sum[rs];
return ;
}
inline int query(int ed,int p=1,int l=1,int r=n){
if(r<=ed) return sum[p];
int mid=(l+r)>>1; push_down(p);
if(ed<=mid) return query(ed,lson);
return query(ed,lson)+query(ed,rson);
}
int st,ed,v;
inline void give_cov(int p=1,int l=1,int r=n){
if(st<=l&&r<=ed) return push_cov(p,v);
int mid=(l+r)>>1; push_down(p);
if(st<=mid) give_cov(lson);
if(ed>mid) give_cov(rson);
return push_up(p);
}
inline void g_cov(int l,int r,int V){
st=l; ed=r; v=V;
give_cov();
}
inline void modify(int pos,int v){
int p=id[pos];
val[id[pos]]=v;
while(p>>=1) push_up(p);
}
}seg;
#undef ls
#undef rs
#undef lson
#undef rson
int dp[N];
signed main(){
freopen("divide.in","r",stdin); freopen("divide.out","w",stdout);
n=read(); rep(i,1,n) a[i]=read();
seg.build(1,1,n);
seg.modify(1,1);
auto ins=[&](int mex,int l,int r){
auto iter=now.lower_bound({mex,0,0});
if(iter==now.end()){
now.insert({mex,l,r});
return ;
}
int L=iter->l,R=iter->r;
if(iter->mex==mex) now.erase(iter),now.insert({mex,L,r});
else now.insert({mex,l,r});
};
int qcnt=0;
for(int i=1;i<=n;++i){
if(a[i]<=n) app[a[i]]=i,lst.modify(a[i],i);
auto iter=now.lower_bound({a[i],0,0});
if(iter!=now.end()){
if(iter->mex==a[i]){
int L=iter->l,R=iter->r,lastv=iter->mex;
now.erase(iter);
while(L<=R){
++qcnt;
int mex=lst.erf(R,lastv+1),pos=max(app[mex]+1,L);
if(pos<=R){
ins(mex,pos,R);
seg.g_cov(pos,R,mex);
}
lastv=mex;
R=app[mex];
}
}
}
seg.g_cov(i,i,!a[i]);
ins(!a[i],i,i);
dp[i]=seg.query(i)%mod;
if(i<n) seg.modify(i+1,dp[i]);
else print(dp[i]);
}
return 0;
}
重排列
考虑到 \(A\) 钦定后的序列中不互质的元素的相对顺序是一定的,从小向大连边得到 \(\rm DAG\) 的每个拓扑序都是 \(B\) 能得到的最终序列
而 \(A\) 能做的工作就是给边重定向来使得最大拓扑序最小
将原图里面的不互质元素连边之后考察每个联通块,从标号最小的点按照出边终点从小到大的顺序遍历整个联通块并进行 \(\rm DAG\) 的构建:如果出边终点已经有入度就不再连边了,否则连边递归处理后继
最后 \(B\) 的操作可以使用优先队列求拓扑序的方式来得到
Code Display
const int N=2010;
int n,a[N],in[N];
vector<int> vec[N],G[N];
bool vis[N];
inline void dfs(int x){
vis[x]=1;
sort(G[x].begin(),G[x].end());
for(auto t:G[x]) if(!vis[t]){
vec[x].emplace_back(t);
in[t]++;
dfs(t);
} return ;
}
signed main(){
freopen("permutation.in","r",stdin); freopen("permutation.out","w",stdout);
n=read();
rep(i,1,n) a[i]=read();
sort(a+1,a+n+1);
rep(i,1,n){
rep(j,i+1,n) if(__gcd(a[i],a[j])!=1){
G[i].emplace_back(j);
G[j].emplace_back(i);
}
}
rep(i,1,n) if(!vis[i]) dfs(i);
priority_queue<int> q;
vector<int> ans;
rep(i,1,n) if(!in[i]) q.push(i);
while(q.size()){
int fr=q.top(); q.pop(); ans.emplace_back(fr);
for(auto t:vec[fr]) if(!(--in[t])) q.push(t);
}
rep(i,0,n-1) print(a[ans[i]]); putchar('\n');
return 0;
}