zoukankan      html  css  js  c++  java
  • SHOI2014 三叉神经树

    传送门

    一道非常好的LCT/树剖题。但是像我这样的菜鸡想不到什么有效做法……

    首先我们可以很容易发现,一次如果要在链上连续修改那么肯定是从底向上的一端连续区间。如果我们把每个节点的输入值作为其权值,那么会被连续更改的一定是一端连续的为1或者为2的区间。

    那么我们就可以通过维护这个区间来解决这道题。如何维护?有一种做法是直接二分深度最大的非1/2的点在哪。不过这个似乎比较麻烦……我们直接维护这个链上深度最深的,非1/2的点的位置。具体怎么维护呢?在(pushup)的时候,我们顺便更新。首先用自己的右儿子更新(深度更大,肯定更优秀),如果不行就用自己,还不行才用左儿子(深度比较小)

    之后具体在处理的时候,我们就是一个标准的LCT操作了。这个题中根始终为1,不需要换根,也不需要(link),(cut)。在修改的时候打通到那个点的路径,之后判断一下是否存在非1/2的点,之后根据情况单点/区间修改即可。

    思路来源于(FlashHu)大佬。看一下代码。

    #include<bits/stdc++.h>
    #define rep(i,a,n) for(register int i = a;i <= n;i++)
    #define per(i,n,a) for(register int i = n;i >= a;i--)
    #define enter putchar('
    ')
    #define I inline
    using namespace std;
    typedef long long ll;
    const int M = 2000005;
    
    I int read()
    {
       int ans = 0,op = 1;char ch = getchar();
       while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
       while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
       return ans * op;
    }
    
    struct edge
    {
        int next,to;
    }e[M<<1];
    
    int ch[M][2],fa[M],val[M],tag[M],num1[M],num2[M],sta[M],top,head[M],ecnt,x,y,z,n,q,ans;
    
    void add(int x,int y) {e[++ecnt] = (edge){head[x],y},head[x] = ecnt;}
    bool nroot(int x) {return (ch[fa[x]][0] == x) || (ch[fa[x]][1] == x);}
    bool get(int x) {return ch[fa[x]][1] == x;}
    void modify(int x,int d) {val[x] ^= 3,swap(num1[x],num2[x]),tag[x] += d;}
    void pushdown(int x){if(tag[x]) modify(ch[x][0],tag[x]),modify(ch[x][1],tag[x]),tag[x] = 0;}
    
    void pushup(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 rotate(int x)
    {
        int y = fa[x],z = fa[y],k = get(x);
        if(nroot(y)) ch[z][get(y)] = x;
        fa[x] = z,ch[y][k] = ch[x][k^1],fa[ch[x][k^1]] = y;
        ch[x][k^1] = y,fa[y] = x;
        pushup(y),pushup(x);
    }
    
    void splay(int x)
    {
        int p = x;sta[++top] = p;
        while(nroot(p)) p = fa[p],sta[++top] = p;
        while(top) pushdown(sta[top--]);
        while(nroot(x))
        {
            int y = fa[x],z = fa[y];
            if(nroot(y)) ((ch[y][0] == x) ^ (ch[z][0] == y)) ? rotate(x) : rotate(y);
            rotate(x);
        }
    }
    
    void access(int x) {for(int g = 0;x;g = x,x = fa[x]) splay(x),ch[x][1] = g,pushup(x);}
    
    void dfs(int x) {for(int i = head[x];i;i = e[i].next) dfs(e[i].to),val[x] += (val[e[i].to] >> 1);}
    
    int main()
    {
        n = read();
        rep(i,1,n) x = read(),y = read(),z = read(),fa[x] = fa[y] = fa[z] = i,add(i,x),add(i,y),add(i,z);
        rep(i,1,(n<<1)+1) x = read(),val[i+n] = x << 1;
        dfs(1),q = read(),ans = val[1] >> 1;
        //rep(i,1,n*3+1) printf("%d ",val[i]);enter;
        while(q--)
        {
            x = read(),val[x] ^= 2;
            int k = val[x] - 1;
            x = fa[x],access(x),splay(x);
            int p = (~k) ? num1[x] : num2[x];
            //rep(i,1,n*3+1) printf("#%d ",num1[i]);enter;
            //rep(i,1,n*3+1) printf("!%d ",num2[i]);enter;
            //printf("%d
    ",p);
            if(!p) modify(x,k),pushup(x),ans ^= 1;
            else splay(p),modify(ch[p][1],k),pushup(ch[p][1]),val[p] += k,pushup(p);
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    添加一个用户到指定用户组: gpasswd –a 用户名 组名 usermod –G 组名 用户名
    MySQL创建用户和授权
    shell
    启动脚本
    TODO
    mysql表分区
    mysql导入千万级数据实操
    mysql快速保存插入大量数据一些方法总结(转)
    MySQL存储过程插入数据过慢处理方法(转)
    mysql备份删库建库导入库
  • 原文地址:https://www.cnblogs.com/captain1/p/10303263.html
Copyright © 2011-2022 走看看