2 条题解
-
0
lzy的题解: /* 思路: 我们要把点的颜色转换成连向父节点的边的颜色,这样每次换颜色就只用处理与一条边的关系,不这样做我们就会被菊花图卡。 所以我们就必须维护有根树(根不能换),不能用makeroot来改变树的形状。 我们定义两棵树,一棵为为黑,一棵为白,每个点会同时在两棵树出现。 因为对于一个有色联通块(树),只有根的父亲的颜色与其他点的父亲的颜色不同。 (联通块的根为1时需要特判) 所以我们每次换颜色只处理一条边的在两棵树上的断(连)就行了。 (一个联通块就是边越来越多,最后汇合而成的)(理由有点牵强,但本题本来就不好讲) 重点在于查询——LCT所维护的森林的每棵树的节点的深度要保证严格递增,但是在树上的联通块的节点可能深度一样。那怎么办呢? 我们在splay节点(trnode)多定义一个变量u,表示其他子树节点的总数——因为父亲只认得两个孩子(悲催的家庭),这样我们就能保证正确性了。 全局(原树)的根设为1。 查询时,我们把x(询问的元素)access并splay上去,再一直往左儿子跳,找到根(不是全局的)。因为根的颜色可能与x同,也可能不同。(只有根为1时,根的颜色才有可能与x的颜色相同) 如果根与x的颜色相同,输出根管辖节点数。否则,输出根的后继管辖的节点数。(后继一定与x有相同颜色) */
#include<cstdio> #include<cstring> #define g getchar() #define lc tr[x].son[0] #define rc tr[x].son[1] using namespace std; const int N=1e5+10; struct edge { int y,next; }a[N<<1];int last[N],len;//邻接表建边 void ins(int x,int y) { a[++len]=(edge){y,last[x]}; last[x]=len; } int n,m,col[N],fa[N]; struct trnode { int f,u,c,son[2]; }; struct link_Cut_Tree { trnode tr[N]; inline void update(int x){tr[x].c=tr[lc].c+tr[rc].c+tr[x].u+1;} inline void rotate(int x,int w) { int f=tr[x].f,ff=tr[f].f,r,R; r=tr[x].son[w];R=f; tr[R].son[1-w]=r; if(r!=0)tr[r].f=R; r=x;R=ff; if(tr[R].son[0]==f)tr[R].son[0]=r; else if(tr[R].son[1]==f)tr[R].son[1]=r;//爷爷不一定认这个孩子 tr[r].f=R; r=f;R=x; tr[R].son[w]=r; tr[r].f=R; update(f); update(x); } inline void splay(int x)//把x旋到所属平衡树的根 { while(tr[tr[x].f].son[0]==x||tr[tr[x].f].son[1]==x)//0从来就不认孩子 { int f=tr[x].f,ff=tr[f].f; if(tr[ff].son[0]!=f&&tr[ff].son[1]!=f) { if(tr[f].son[0]==x)rotate(x,1); else rotate(x,0); } else { if(tr[ff].son[0]==f&&tr[f].son[0]==x)rotate(f,1),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[1]==x)rotate(f,0),rotate(x,0); else if(tr[ff].son[0]==f&&tr[f].son[1]==x)rotate(x,0),rotate(x,1); else rotate(x,1),rotate(x,0); } } } inline void access(int x) { int y=0; while(x!=0) { splay(x); tr[x].u+=tr[rc].c-tr[y].c; tr[x].son[1]=y; update(x); y=x;x=tr[x].f; } } inline void link(int x) { if(!fa[x])return ; int y=fa[x]; access(y);splay(y);splay(x);//x和y还不在一棵树上 tr[x].f=y;tr[y].u+=tr[x].c;update(y); } inline void cut(int x) { if(!fa[x])return ; access(x);splay(x); tr[lc].f=0;lc=0; update(x); } inline int find(int x) { access(x);splay(x); int y=x; while(tr[y].son[0])y=tr[y].son[0]; splay(y); if(col[x]==col[y])return tr[y].c; else return tr[tr[y].son[1]].c; } }LCT[2];//1为黑 inline void dfs(int x) { for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]) { fa[y]=x; LCT[1].link(y); dfs(y); } } } inline void qr(int &x) { char c=g;x=0; while(!('0'<=c&&c<='9'))c=g; while('0'<=c&&c<='9')x=x*10+c-'0',c=g; } inline void write(int x) { if(x/10)write(x/10); putchar(x%10+'0'); } int main() { qr(n); len=0;memset(last,0,sizeof(last)); int x,y; for(int i=1;i<n;i++)qr(x),qr(y),ins(x,y),ins(y,x); for(int i=1;i<=n;i++)LCT[1].tr[i].c=LCT[0].tr[i].c=col[i]=1; dfs(1); qr(m); while(m--) { qr(y);qr(x); switch(y){ case 0:write(LCT[col[x]].find(x));puts("");break; case 1:LCT[col[x]].cut(x);col[x]^=1;LCT[col[x]].link(x);break; } } return 0; }chenlibangdn的题解: /这道题还是按照前面QTREE4-5的做法 颜色0/1=黑/白 lsum[x][w]表示这条链下面颜色为w且联通的点的个数 rsum[x][w]表示这条链上面颜色为w且联通的点的个数 c[x]表示点x的儿孙个数 sum[x][w]表示x的儿孙中颜色为w的个数 然后仿照前面的维护一下就好了。/
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> using namespace std; template<typename T>inline void qr(T &x){ x=0;int f=0;char s=getchar(); while(s<'0'||'9'<s)f|=s=='-',s=getchar(); while('0'<=s&&s<='9')x=x*10+s-48,s=getchar(); x=f?-x:x; } const int mxn=1e5+10; int n,col[mxn]; int tot,hd[mxn],ver[mxn],nxt[mxn]; int fa[mxn],c[mxn],sum[mxn][2],lsum[mxn][2],rsum[mxn][2]; int a[mxn][2],ch[mxn][2]; #define lc ch[x][0] #define rc ch[x][1] #define ls(x) ch[x][0] #define rs(x) ch[x][1] #define rep for(int w=0;w<=1;w++) #define okl (col[x]==w)*(c[lc]==sum[lc][w]) #define okr (col[x]==w)*(c[rc]==sum[rc][w]) bool nrt(int x){return ls(fa[x])==x||rs(fa[x])==x;} void update(int x){ c[x]=c[lc]+c[rc]+1; rep sum[x][w]=sum[lc][w]+sum[rc][w]+(col[x]==w); rep lsum[x][w]=lsum[rc][w]+okr*(1+a[x][w]+lsum[lc][w]); rep rsum[x][w]=rsum[lc][w]+okl*(1+a[x][w]+rsum[rc][w]); } void rotate(int x){ int y=fa[x],z=fa[y],w=rs(y)==x; if(nrt(y))ch[z][rs(z)==y]=x;fa[x]=z; fa[ch[y][w]=ch[x][1-w]]=y; ch[x][1-w]=y,fa[y]=x; update(y); } void splay(int x){ while(nrt(x)){ int y=fa[x],z=fa[y]; if(nrt(y)) (rs(z)==y)^(rs(y)==x)?rotate(x):rotate(y); rotate(x); } update(x); } void access(int x){ for(int y=0;x;x=fa[y=x]){ splay(x); if(y) rep a[x][w]-=rsum[y][w]; if(rc) rep a[x][w]+=rsum[rc][w]; rc=y;update(x); } } void modify(int x){ access(x); splay(x);col[x]=1-col[x]; update(x); } int query(int x){ access(x); splay(x); return 1+a[x][col[x]]+lsum[lc][col[x]]+rsum[rc][col[x]]; } void add(int x,int y){ ver[++tot]=y;nxt[tot]=hd[x];hd[x]=tot; } void maketree(int x){ for(int i=hd[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa[x])continue; fa[y]=x; maketree(y); rep a[x][w]+=a[y][w]+(col[y]==w); } update(x); } int main(){ qr(n); for(int i=1;i<n;i++){ int x,y;qr(x),qr(y); add(x,y),add(y,x); } maketree(1); int m;qr(m); while(m--){ int x,op; qr(op),qr(x); if(op)modify(x); else printf("%d\n",query(x)); } return 0; } -
0
lzy:
/* 思路: 我们要把点的颜色转换成连向父节点的边的颜色,这样每次换颜色就只用处理与一条边的关系,不这样做我们就会被菊花图卡。 所以我们就必须维护有根树(根不能换),不能用makeroot来改变树的形状。 我们定义两棵树,一棵为为黑,一棵为白,每个点会同时在两棵树出现。 因为对于一个有色联通块(树),只有根的父亲的颜色与其他点的父亲的颜色不同。 (联通块的根为1时需要特判) 所以我们每次换颜色只处理一条边的在两棵树上的断(连)就行了。 (一个联通块就是边越来越多,最后汇合而成的)(理由有点牵强,但本题本来就不好讲) 重点在于查询——LCT所维护的森林的每棵树的节点的深度要保证严格递增,但是在树上的联通块的节点可能深度一样。那怎么办呢? 我们在splay节点(trnode)多定义一个变量u,表示其他子树节点的总数——因为父亲只认得两个孩子(悲催的家庭),这样我们就能保证正确性了。 全局(原树)的根设为1。 查询时,我们把x(询问的元素)access并splay上去,再一直往左儿子跳,找到根(不是全局的)。因为根的颜色可能与x同,也可能不同。(只有根为1时,根的颜色才有可能与x的颜色相同) 如果根与x的颜色相同,输出根管辖的节点数。否则,输出根的后继管辖的节点数。(后继一定与x有相同颜色) */ #include<cstdio> #include<cstring> #define g getchar() #define lc tr[x].son[0] #define rc tr[x].son[1] using namespace std; const int N=1e5+10; struct edge { int y,next; }a[N<<1];int last[N],len;//邻接表建边 void ins(int x,int y) { a[++len]=(edge){y,last[x]}; last[x]=len; } int n,m,col[N],fa[N]; struct trnode { int f,u,c,son[2]; }; struct link_Cut_Tree { trnode tr[N]; inline void update(int x){tr[x].c=tr[lc].c+tr[rc].c+tr[x].u+1;} inline void rotate(int x,int w) { int f=tr[x].f,ff=tr[f].f,r,R; r=tr[x].son[w];R=f; tr[R].son[1-w]=r; if(r!=0)tr[r].f=R; r=x;R=ff; if(tr[R].son[0]==f)tr[R].son[0]=r; else if(tr[R].son[1]==f)tr[R].son[1]=r;//爷爷不一定认这个孩子 tr[r].f=R; r=f;R=x; tr[R].son[w]=r; tr[r].f=R; update(f); update(x); } inline void splay(int x)//把x旋到所属平衡树的根 { while(tr[tr[x].f].son[0]==x||tr[tr[x].f].son[1]==x)//0从来就不认孩子 { int f=tr[x].f,ff=tr[f].f; if(tr[ff].son[0]!=f&&tr[ff].son[1]!=f) { if(tr[f].son[0]==x)rotate(x,1); else rotate(x,0); } else { if(tr[ff].son[0]==f&&tr[f].son[0]==x)rotate(f,1),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[1]==x)rotate(f,0),rotate(x,0); else if(tr[ff].son[0]==f&&tr[f].son[1]==x)rotate(x,0),rotate(x,1); else rotate(x,1),rotate(x,0); } } } inline void access(int x) { int y=0; while(x!=0) { splay(x); tr[x].u+=tr[rc].c-tr[y].c; tr[x].son[1]=y; update(x); y=x;x=tr[x].f; } } inline void link(int x) { if(!fa[x])return ; int y=fa[x]; access(y);splay(y);splay(x);//x和y还不在一棵树上。为什么x不用access呢?因为x还未向原树的父亲连边,x为所属splay中深度最小的点,整棵splay的根的父亲为0。 tr[x].f=y;tr[y].u+=tr[x].c;update(y); } inline void cut(int x) { if(!fa[x])return ; access(x);splay(x);//y一定在x的左子树内。因为之前tr[x].f=y,使得access时一定能遍历到fa[x]。 tr[lc].f=0;lc=0;//直接断开与父亲那一边的联系,tr[x].son[0]不一定为fa[x],但没关系. update(x); } inline int find(int x) { access(x);splay(x); int y=x; while(tr[y].son[0])y=tr[y].son[0]; splay(y); if(col[x]==col[y])return tr[y].c; else return tr[tr[y].son[1]].c; } }LCT[2];//1为黑 inline void dfs(int x) { for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]) { fa[y]=x; LCT[1].link(y); dfs(y); } } } inline void qr(int &x) { char c=g;x=0; while(!('0'<=c&&c<='9'))c=g; while('0'<=c&&c<='9')x=x*10+c-'0',c=g; } inline void write(int x) { if(x/10)write(x/10); putchar(x%10+'0'); } int main() { qr(n); len=0;memset(last,0,sizeof(last)); int x,y; for(int i=1;i<n;i++)qr(x),qr(y),ins(x,y),ins(y,x); for(int i=1;i<=n;i++)LCT[1].tr[i].c=LCT[0].tr[i].c=col[i]=1; dfs(1); qr(m); while(m--) { qr(y);qr(x); switch(y){ case 0:write(LCT[col[x]].find(x));puts("");break; case 1:LCT[col[x]].cut(x);col[x]^=1;LCT[col[x]].link(x);break; } } return 0; }chenlibangdn:
/*这道题还是按照前面QTREE4-5的做法 颜色0/1=黑/白 lsum[x][w]表示这条链下面颜色为w且联通的点的个数 rsum[x][w]表示这条链上面颜色为w且联通的点的个数 c[x]表示点x的儿孙个数 sum[x][w]表示x的儿孙中颜色为w的个数 然后仿照前面的维护一下就好了。*/ #include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> using namespace std; template<typename T>inline void qr(T &x){ x=0;int f=0;char s=getchar(); while(s<'0'||'9'<s)f|=s=='-',s=getchar(); while('0'<=s&&s<='9')x=x*10+s-48,s=getchar(); x=f?-x:x; } const int mxn=1e5+10; int n,col[mxn]; int tot,hd[mxn],ver[mxn],nxt[mxn]; int fa[mxn],c[mxn],sum[mxn][2],lsum[mxn][2],rsum[mxn][2]; int a[mxn][2],ch[mxn][2]; #define lc ch[x][0] #define rc ch[x][1] #define ls(x) ch[x][0] #define rs(x) ch[x][1] #define rep for(int w=0;w<=1;w++) #define okl (col[x]==w)*(c[lc]==sum[lc][w]) #define okr (col[x]==w)*(c[rc]==sum[rc][w]) bool nrt(int x){return ls(fa[x])==x||rs(fa[x])==x;} void update(int x){ c[x]=c[lc]+c[rc]+1; rep sum[x][w]=sum[lc][w]+sum[rc][w]+(col[x]==w); rep lsum[x][w]=lsum[rc][w]+okr*(1+a[x][w]+lsum[lc][w]); rep rsum[x][w]=rsum[lc][w]+okl*(1+a[x][w]+rsum[rc][w]); } void rotate(int x){ int y=fa[x],z=fa[y],w=rs(y)==x; if(nrt(y))ch[z][rs(z)==y]=x;fa[x]=z; fa[ch[y][w]=ch[x][1-w]]=y; ch[x][1-w]=y,fa[y]=x; update(y); } void splay(int x){ while(nrt(x)){ int y=fa[x],z=fa[y]; if(nrt(y)) (rs(z)==y)^(rs(y)==x)?rotate(x):rotate(y); rotate(x); } update(x); } void access(int x){ for(int y=0;x;x=fa[y=x]){ splay(x); if(y) rep a[x][w]-=rsum[y][w]; if(rc) rep a[x][w]+=rsum[rc][w]; rc=y;update(x); } } void modify(int x){ access(x); splay(x); col[x]=1-col[x]; update(x); } int query(int x){ access(x); splay(x); return 1+a[x][col[x]]+lsum[lc][col[x]]+rsum[rc][col[x]]; } void add(int x,int y){ ver[++tot]=y; nxt[tot]=hd[x]; hd[x]=tot; } void maketree(int x){ for(int i=hd[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa[x])continue; fa[y]=x; maketree(y); rep a[x][w]+=a[y][w]+(col[y]==w); } update(x); } int main(){ qr(n); for(int i=1;i<n;i++){ int x,y;qr(x),qr(y); add(x,y),add(y,x); } maketree(1); int m;qr(m); while(m--){ int x,op; qr(op),qr(x); if(op)modify(x); else printf("%d\n",query(x)); } return 0; }
- 1
信息
- ID
- 550
- 时间
- 1000ms
- 内存
- 2048MiB
- 难度
- 10
- 标签
- 递交数
- 49
- 已通过
- 1
- 上传者