洛谷题目传送门ui
你谷无题解因而来补一发spa
随便百度题解,发现了很多诸如树剖\(log^3\)LCT\(log^2\)的可怕描述。。。。。。code
因而来想一想怎么利用题目的性质,把复杂度降下来。排序
首先,每一个点的输出状态只有\(0/1\),因而每一个点的总状态也很是有限,能够根据权值为\(1\)的儿子数量\(0-3\)分为四种,记为该点的点权。ip
咱们都会模拟暴力过程——先改叶子节点(先默认为\(0\)改成\(1\)),若是它的父亲此时权值为\(1\)的儿子数量从原来小于\(0\)的变成大于\(0\)的,那么父亲的权值也要改。以此类推,直到有一个节点输出状态没有变化,那么它的全部祖先确定不会变。get
经过模拟咱们发现,每次修改的必定是一段自底向上的连续区间!it
接着也就不难想到,只有当点权为\(1\)时,才能经过修改点权变成\(2\),使输出由\(0\)变成\(1\),从而继续引起祖先的变化。那么咱们须要知道的就是,对于每个叶子节点,它自底向上的连续一段点权为\(1\)的部分。io
再讨论叶子节点\(1\)改\(0\)的状况,同理也能够发现咱们还要维护自底向上的连续一段点权为\(2\)的部分。ast
这个能够树剖(有不少维护法,都是\(log^2\)的,跳链和链修改都有\(log\))正在学树剖,先留个坑,到时候再补。。。class
固然能够LCT,讲两个维护法。第一种是用bool值维护区间是否有权值不为\(1/2\)的点,每次Splay上二分查找最深的不为\(1/2\)的点,把它伸展上来,右子树作区间修改,这个点作单点修改。
由于写二分比较麻烦(其实就是几行的事),因此还不如直接维护最深的不为\(1/2\)点的编号,找都不用找。直接把它伸展上来。修改同上。容易发现这里的LCT连换根都不要。
两种写法都须要注意特判:若是整条从根到叶子的链没有一个不为\(1/2\)的点,直接作区间修改。
分享一个naive的错误——蒟蒻默认父节点的编号比子节点小,而后pushup直接取\(\max\),居然得到了95分?!调了半天本机对拍又是全AC(本身的数据生成器确定是父节点的编号比子节点小啦。。。)
刚掉这题后还收获了一点小经验——不要给LCT永久化地贴上常数大的标签!由于少一个\(log\),因此\(n\)越大越有优点(这题\(5*10^5\)),还不用reverse。看看统计,就知道什么叫LCT全方位(时间、空间、码量)完爆树剖的感受了哈哈哈哈hhhh
https://www.luogu.org/recordnew/lists?uid=&pid=P4332&status=&sort=1
https://loj.ac/problem/2187/statistics/fastest
#include<cstdio> #include<algorithm> #define RG register #define I inline #define R RG int #define lc c[x][0] #define rc c[x][1] #define G if(++ip==ie)if(fread(ip=ibuf,1,L,stdin)) using namespace std; const int N=5e5+9,M=1.5e6+9,L=1<<19; char ibuf[L],*ie=ibuf+L,*ip=ie-1; int n,f[M],c[N][2],t[N],n1[N],n2[N],v[M],q[M],d[N]; I int max(R x,R y){return x>y?x:y;} I int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){(x*=10)+=*ip&15;G;} return x; } I bool nrt(R x){ return c[f[x]][0]==x||c[f[x]][1]==x; } I void up(R x){//先右儿子再本身最后左儿子 if(!(n1[x]=n1[rc])&&!(n1[x]=x*(v[x]!=1)))n1[x]=n1[lc]; if(!(n2[x]=n2[rc])&&!(n2[x]=x*(v[x]!=2)))n2[x]=n2[lc]; } I void dn(R x,R tg){//被区间修改的要么都是1要么都是2,直接反转信息 v[x]^=3;swap(n1[x],n2[x]);t[x]+=tg; } I void all(R x){ if(nrt(x))all(f[x]); if(t[x])dn(lc,t[x]),dn(rc,t[x]),t[x]=0; } I void rot(R x){ R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k]; if(nrt(y))c[z][c[z][1]==y]=x; f[f[f[c[c[x][!k]=y][k]=w]=y]=x]=z;up(y); } I void sp(R x){ all(x); for(R y;nrt(x);rot(x)) if(nrt(y=f[x]))rot((c[f[y]][0]==y)^(c[y][0]==x)?x:y); up(x); } I void ac(R x){ for(R y=0;x;sp(x),rc=y,up(y=x),x=f[x]); } int main(){ n=in();R he,tl=0,i,x,tp,nowrt;//nowrt全局记录根的输出,方便,减少常数 for(i=1;i<=n;++i)d[f[in()]=f[in()]=f[in()]=i]=3; for(;i<=3*n+1;++i)v[q[++tl]=i]=in()<<1; for(he=1;he<=tl;++he){//懒得dfs了,直接从下往上拓扑排序预处理 x=q[he];if(x<=n)up(x); v[f[x]]+=v[x]>>1; if(!--d[f[x]])q[++tl]=f[x]; } nowrt=v[1]>>1; for(R q=in();q;--q){ tp=(v[x=in()]^=2)-1;//记录当前变化类型 ac(x=f[x]);sp(x); if((~tp?n1:n2)[x]){ sp(x=(~tp?n1:n2)[x]); dn(rc,tp),up(rc); v[x]+=tp;up(x); } else dn(x,tp),up(x),nowrt^=1;//注意特判 putchar(nowrt|'0');putchar('\n'); } return 0; }