【HYSBZ 2759】一道动态树的好题

题目大意

给出 n 个变量xi对应的形如 xi=kixj+bi(mod10007) 的方程,要求实现 m 个操作,包括以下两种。

  • 解出xi的值,或输出无解或无穷解

    • 将第 i 个方程改为xi=kixj+bi(mod10007)
    • n<30000,m<100000


      分析

      这道题还是比较显然的。
      我们发现每个变量都依赖于另一个变量的值。那么他们之间连边以后必定形成若干个环套树,且边都是有向的。
      不妨用LCT来维护这个图。
      从某个点 x 出发,一直走必定是一个ρ型。那么我们就可以解出那个环接入点的变量的值,从而算出 x 的值。
      沿着这个思路,我们用LCT来维护一条链。而链顶连回这条链中的某个点,我们需要记录一下链顶连到的是哪个点,而且这个信息是和LCT的部分没有任何关系的。
      维护的部分,沿着一定的顺序一一合并两棵子树的信息就可以了。
      我的写法里面定义了如此几个函数:

      getself(x)计算了 x 这个变量的值,前提是x是接入点。具体是 access(x) 以后,解一个同余方程就可以了。

      由此也可见,我是将一棵环套树上面的环剖开了,无视某条边来进行维护。

      gettail(x) 返回的是从 x 这个点开始向上跳达到的链顶。

      query(x)是计算任意变量 x 的值。具体是getself(gettail(x))以后再 access(x) 并计算。

      isancestor(x,y) 返回 x 是不是y的某个祖先,具体来说是 access(y) splay(x) 再判 x 是不是y的右儿子。

      change(x) 这个函数是改变 x 这个变量的信息。这里要分成两种情况来处理,分别是

      • x在环上,那么此时的接入点就要被修改了。

      • x 不在环上,那么原来的环就不会被影响,link一下 x 就好了。

      还有预处理部分需要判一下isancestor再去决定是应该 link 还是设为接入点。

      至此,这个问题就基本上解决了。
      时间复杂度 O(mlogn)

      以下贴一下代码。

      #include <cstdio>
      #include <cstdlib>
      #include <iostream>
      #include <algorithm>
      #include <utility>
      #include <cstring>
      #include <bitset>
      #include <string>
      #include <vector>
      #include <queue>
      #include <map>
      #include <set>
      
      typedef double db;
      typedef long long LL;
      typedef std :: pair< int, int > PII;
      typedef std :: pair< LL, LL > PLL;
      typedef std :: pair< db, db > PDD;
      
      const db dInf = 1E90;
      const LL lInf = ( LL ) 1E16;
      const int Inf = 0x23333333;
      const int N = 30005, Mo = 10007;
      
      #define it iterator
      #define rbg rbegin()
      #define ren rend()
      #define bg begin()
      #define en end()
      #define sz size()
      #define fdi( i, x ) for ( typeof( x.rbg ) i=x.rbg; i!=x.ren; ++i )
      #define foi( i, x ) for ( typeof( x.bg ) i=x.bg; i!=x.en; ++i )
      #define fd( i, y, x ) for ( int i=( y )-1, lim=x; i>=lim; --i )
      #define fo( i, x, y ) for ( int i=x, lim=y; i<lim; ++i )
      #define mkp( A, B ) make_pair( A, B )
      #define pub( x ) push_back( x )
      #define pob( x ) pop_back( x )
      #define puf( x ) push_front( x )
      #define pof( x ) pop_front( x )
      #define fi first
      #define se second
      
      inline int pwr( const int &x, const int &p )
      {   
          if ( !p ) return 1;
          int temp = pwr( x, p >> 1 );
          temp = temp * temp % Mo;
          if ( p & 1 ) temp = temp * x % Mo;
          return temp;
      }
      
      int gcd( const int A, const int B )
      {
          return ( B ? gcd( B, A % B ) : A );
      }
      
      namespace LCT
      {
          struct node
          {
              int fa, s[2];
              int _k, _b, k, b;
          } a[N];
          int grpfa[N];
      
          inline void update( const int &x )
          {
              a[x]._k = a[x].k, a[x]._b = a[x].b;
              const int &l = a[x].s[0], &r = a[x].s[1];
              if ( l ) 
              {
                  a[x]._k = ( a[x]._k * a[l]._k ) % Mo;
                  a[x]._b = ( a[l]._k * a[x]._b + a[l]._b ) % Mo;
              }
              if ( r ) 
              {
                  a[x]._b = ( a[x]._k * a[r]._b + a[x]._b ) % Mo;
                  a[x]._k = ( a[x]._k * a[r]._k ) % Mo;
              }
          }
      
          inline bool top( const int &x )
          {
              const int &y = a[x].fa;
              return ( !y ) || a[y].s[0]!=x && a[y].s[1]!=x;
          }
      
          inline int dir( const int &x )
          {
              const int &y = a[x].fa;
              return a[y].s[1] == x;
          }
      
          inline void rotate( const int &x )
          {   
              int y = a[x].fa, z = a[y].fa;
              bool p = dir( x ), q = p ^ 1;
              int A = a[x].s[q];
              if ( !top( y ) ) a[z].s[ dir( y ) ] = x;
              a[y].s[p] = A, a[y].fa = x;
              a[x].s[q] = y, a[x].fa = z;
              if ( A ) a[A].fa = y;
              update( y );
          }
      
          inline void splay( const int &x )
          {   
              while ( !top( x ) )
              {
                  int y = a[x].fa, z = a[y].fa;
                  if ( top( y ) ) rotate( x );
                  else if ( top( z ) || dir( y )^dir( x ) ) 
                      rotate( x ), rotate( x );
                  else rotate( y ), rotate( x );
              }
              update( x );
          }
      
          inline void access( const int &x )
          {   
              for ( int _x=x, y=0; _x; _x=a[_x].fa )
                  splay( _x ), a[_x].s[0] = y, y = _x;
              splay( x );
          }
      
          inline int getself( const int &x )
          {
              access( x );
              int k = ( 1 - a[x]._k + Mo ) % Mo, b = a[x]._b;
              if ( gcd( k, Mo )!=1 ) return ( b ? -1 : -2 );
              else return b * pwr( k, Mo - 2 ) % Mo;
          }
      
          inline int gettail( const int &x )
          {
              int _x = x; access( x );
              for ( ; a[_x].s[1]; _x=a[_x].s[1] );
              splay( _x );
              return _x;
          }
      
          inline int query( const int &x )
          {
              int core = getself( grpfa[ gettail( x ) ] );
              if ( core < 0 ) return core;
              access( x );
              return ( a[x]._k * core + a[x]._b ) % Mo;
          }
      
          inline bool isancestor( const int &x, const int &y )
          {
              if ( x==y ) return 1;
              access( y ), splay( x );
              return a[x].s[0] == y;
          }
      
          inline void change( const int &x, const int &_y, const int &k, const int &b )
          {
              int tail = gettail( x );
              bool inloop = isancestor( x, grpfa[tail] );
              access( x ), a[x].s[1] = a[ a[x].s[1] ].fa = 0;
              if ( inloop ) 
              {
                  access( tail );
                  a[tail].fa = grpfa[tail], grpfa[tail] = 0;
              }
              a[x].fa = 0;
              if ( isancestor( x, _y ) ) grpfa[x] = _y;
              else a[x].fa = _y; 
              a[x].k = k, a[x].b = b, access( x );
          }
      }
      
      int n;
      
      void preprocessing()
      {
          int fa; scanf( "%d", &n ), ++n;
          fo ( i, 1, n ) 
          {
              scanf( "%d%d%d", &LCT :: a[i].k, &fa, &LCT :: a[i].b );
              if ( LCT :: isancestor( i, fa ) ) LCT :: grpfa[i] = fa;
              else LCT :: a[i].fa = fa;
          }
      }
      
      void solve()
      {
          char type;
          int T, x, y, k, b; 
          scanf( "%d", &T );
          fo ( Case, 0, T )
          {
              for ( type='`'; type!='A' && type!='C'; type=getchar() );
              if ( type=='A' ) 
              {
                  scanf( "%d", &x );
                  printf( "%d\n", LCT :: query( x ) );
              }
              else 
              {
                  scanf( "%d%d%d%d", &x, &k, &y, &b );
                  LCT :: change( x, y, k, b );
              }
          }
      }
      
      int main()
      {
          preprocessing();
          solve();
          return 0;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值