1 条题解
-
0
首先一条链可以尝试二分找第一个 OR,即选一个前缀,每个点挂的叶子填 ,链下方的点填 ,那么可以判断前缀中是否有 OR,找到 OR 后,可以同时翻转该点以及其两个儿子使其变成 AND,然后就能继续二分了。
然后考虑完美二叉树的情况,那么依旧要二分,发现我们可以从上往下对每层的点二分,具体来说之前每层的点已经知道符号,所有可以全部变成 OR,然后这层要检验的每个点一个子树全填 ,一个子树全填 ,就能判断这些点中有没有 OR。
一般的情况考虑拼合两种构造,我们可以选若干条没有祖先后代关系的链,要求这些点的祖先都已经被确定,此时就能在这个点集上二分了。
具体就是每条链用第一种方法构造,使得有 OR 时链顶为 ,然后每个点的所有祖先填 OR 即可。
此时我们把图分成 个点集就会使用 次额外的询问,注意到取出原树的重链剖分,按到根轻边个数分组恰好满足题意,则交互次数 。
注意到瓶颈在第一部分,这种二分求多个物品的问题,一个经典优化就是顶层分块,我们取块长 ,然后把每个点集拆成若干 的段,然后每段内部依次二分,则交互次数 ,取 可以通过过。
时间复杂度 。
代码:
#include<bits/stdc++.h> #include "circuit.h" using namespace std; const int MAXN=16005,B=64; basic_string <int> G[MAXN],ch[MAXN],id[MAXN],E[MAXN]; int n,siz[MAXN],hson[MAXN]; void dfs1(int u) { siz[u]=1,hson[u]=n; for(int v:G[u]) { dfs1(v),siz[u]+=siz[v]; if(siz[v]>siz[hson[u]]) hson[u]=v; } } void dfs2(int u,int d) { ch[d].push_back(u); if(hson[u]<n) dfs2(hson[u],d); for(int v:G[u]) if(v^hson[u]) dfs2(v,d+1); } string solve(int N,int,vector<int>L,vector<int>R) { for(int i=0;i<N;++i) for(int x:{L[i],R[i]}) if(x<N) G[i].push_back(x); for(int i=N;i<=2*N;++i) E[i]={i}; for(int i=N-1;~i;--i) E[i]=E[L[i]]+E[R[i]]; n=N,dfs1(0),dfs2(0,0); int m=0; for(int d=0;d<20;++d) if(ch[d].size()) { for(int x:ch[d]) { id[m].push_back(x); if((int)id[m].size()==B) ++m; } m++; } string ans=string(n,'&'); for(int d=0;d<m;++d) if(id[d].size()) { string qy=string(2*n+1,'0'); for(int c=0;c<d;++c) for(int x:id[c]) { if(ans[x]=='&') qy[x]^=1,qy[L[x]]^=1,qy[R[x]]^=1; } auto chk=[&](int x) { string t=qy; for(int i=0;i<=x;++i) { int u=id[d][i]; if(hson[u]<n) { for(int o:E[L[u]^R[u]^hson[u]]) t[o]^=1; } else t[L[u]]^=1; } return query(t); }; int sz=id[d].size()-1,p=-1; while(chk(sz)) { int x=sz; for(int k=1<<15;k;k>>=1) if(x-k>p&&chk(x-k)) x-=k; int u=id[d][x]; p=x; ans[u]='|',qy[u]^=1,qy[L[u]]^=1,qy[R[u]]^=1; } } return ans; }
- 1
信息
- ID
- 8382
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者