【考試總結】2022-03-28
逃離藏寶洞
參考 \(\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;
}