洛谷4322 SHOI2014 三叉神经树(LCT+思维)

题目链接

好久之前做的题了QWQ
现在来补一发博客

一道神仙题啊。。qwq

首先,我们可以看出来,我们如果对于每个点维护一个 v a l val ,表示他的直系儿子中有几个表现为1的。

那么 v a l [ x ] > > 1 val[x]>>1 就是他反应的类型

这样十分便于我们计算一开始的 v a l val

那么考虑修改。

一定是会修改一条 1 0 > 1 ) 2 ( 1 > 0 ) 连续1(对应着0->1),或者连续2(1->0)

也就是说,如果我们能够知道一次修改, 1 x 1到x 的路径下最下面的1或者2的位置,我们就能够通过链修改来实现。

其实一开始我想的是二分

我们发现,可以通过 L C T LCT 维护最深的不是 1 1 的位置和不是 2 2 的位置 n u m 1 n u m 2 num1和num2

那么我们对于一次修改,假设由 0 1 0修改成1
如果 n u m 1 = = 0 num1==0 ,那么说明整条链都会被修改,直接修改整条路径

不然,我们就将路径提出来, s p l a y ( n u m 1 ) splay(num1) 之后,修改他的右儿子,表示他下面的点。
然后把当前点的 v a l val 修改,但是不改变别的量。

QWQ有一些细节,对于修改的时候,由于路径上的 v a l val 都是1或者2。
所以修改的之后可以直接 x o r   2 xor\ 2

具体细节看代码实现吧

里面有详细的注释

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e6+1e2;
int ch[maxn][3];
int fa[maxn],val[maxn];
int tag[maxn];
int n,m;
int num1[maxn],num2[maxn]; //深度最深的 儿子数不为1 或者 2 的 节点是的编号 
int st[maxn];
int son(int x)
{
 if (ch[fa[x]][0]==x) return 0;
 else return 1;
}
bool notroot(int x)
{
 return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x) //由于是深度最深,我们一定是先考虑右子树,再说当前点,再是右子树 
{
 num1[x]=num1[ch[x][1]];
 if (!num1[x] && val[x]!=1) num1[x]=x;
 if (!num1[x]) num1[x]=num1[ch[x][0]];
 num2[x]=num2[ch[x][1]];
 if (!num2[x] && val[x]!=2) num2[x]=x;
 if (!num2[x]) num2[x]=num2[ch[x][0]];
}
void solve(int x,int d)
{
 val[x]^=3;
 swap(num1[x],num2[x]); //修改的时候,必定是一段全为1或者2的区间,所以一个一定是0,直接交换是没错的 
 tag[x]+=d;
}
void pushdown(int x)
{
 if (tag[x])
 {
  if (ch[x][0]) solve(ch[x][0],tag[x]);
  if (ch[x][1]) solve(ch[x][1],tag[x]);
  tag[x]=0;
 }
}
void rotate(int x)
{
 int y=fa[x],z=fa[y];
 int b=son(x),c=son(y);
 if (notroot(y)) ch[z][c]=x;
 fa[x]=z;
 ch[y][b]=ch[x][!b];
 fa[ch[x][!b]]=y;
 ch[x][!b]=y;
 fa[y]=x;
 update(y);
 update(x);
}
void splay(int x)
{
 int y=x,cnt=0;
 st[++cnt]=y;
 while (notroot(y)) y=fa[y],st[++cnt]=y;
 while (cnt) pushdown(st[cnt--]);
 while (notroot(x))
 {
  int y=fa[x],z=fa[y];
  int b=son(x),c=son(y);
  if (notroot(y))
  {
   if (b==c) rotate(y);
   else rotate(x);
  }
  rotate(x);
 }
    update(x);
}
void access(int x)
{
 for (int y=0;x;y=x,x=fa[x])
 {
  splay(x);
  ch[x][1]=y;
  update(x);
 }
}
int in[maxn];
queue<int> q;
int main()
{
   n=read();
   for (int i=1;i<=n;i++)
   {
      int x=read(),y=read(),w=read();     
      in[i]=3;
      fa[x]=fa[y]=fa[w]=i; 
   }
   for (int i=n+1;i<=3*n+1;i++) val[i]=read()*2,q.push(i); //我们事先val都*2,那么对于每个点,他表现出来的特征就是val>>1 
   int m=read();
   while (!q.empty()) //拓扑排序先预处理出来每个点的val 
   {
      int x=q.front();
      q.pop();
      if (x<=n) update(x);
      val[fa[x]]+=val[x]/2;
      in[fa[x]]--;
      if (!in[fa[x]]) q.push(fa[x]);
   }
   int ans=val[1]>>1;
   //在本题中,val表示儿子的表示1的数量,那么val>>1就相当于每个点表达的信息
   for (int i=1;i<=m;i++)
   {
      int x=read();
      val[x]^=2; //叶子节点只有可能是0或者1,而乘2之后就是0或者2 
      int k = val[x] - 1;
      x=fa[x]; //不修改底下的叶子节点 
      access(x);
      splay(x); //打通这个点到1的路径 
      int now;
      if (k==-1) now = num2[x];
      else now = num1[x];
      if (!now)
      {
          solve(x,k);
          update(x);
          ans^=1;
   }
   else
   {
     splay(now);
     solve(ch[now][1],k); 
     update(ch[now][1]);
   val[now]+=k;
     update(now);
   }
   cout<<ans<<"\n";
   }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/84782812