SHOI2014 三叉神经树

Posted captain1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

以上是关于SHOI2014 三叉神经树的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj3553] [Shoi2014]三叉神经树

[BZOJ 3553][SHOI2014]三叉神经树

SHOI2014 三叉神经树

洛谷 P4332 [SHOI2014]三叉神经树 题解

BZOJ-3553三叉神经树 树链剖分

解题:SHOI 2014 概率充电器