hdu 5267 pog loves szh IV(点分治+线段树)

好久之前写的题了。现在要重新开始写题解了。就从这题开始吧。

题意:一棵树,两点之间的权值为路径上的值异或得到,求任意两点之间的权值和,有修改操作,每次都要输出任意两点间的权值和。

做法:当时写这题写了好久。就说下一些关键的思路,因为细节部分真的是太多。首先不同的位独立考虑,然后任意两点我们肯定会想到点分治,点分治时会dfs求出子树里每个点到根节点的权值,然后进行配对求经过根节点的权值和。

那么这个题我们可以把分治的的过程给保存下来,因为修改一个点的权值就意味着在某一个点分治的阶段,那个点其下的点到根节点的权值全都异或1(因为按位不同考虑,假设那个位跟原来不一样)。所以可以弄出dfs序,然后区间更新即可。然后每一次分治的根节点我们要保存:其下1和0的个数,1和1配对的个数,0个0配对的个数,1和0配对的个数。那么在修改时,我们先把那颗子树上0和1对于这个根节点的贡献给消除,再去在子树上区间更新得到新的0和1的个数,再去重新计算根节点我要维护的那几个值。这样答案就可以重新知道了。

具体思路就是这样,但是比较繁琐,比如得维护每个点的每一次分治是属于哪个线段树,以及它在这个线段树中所能影响的范围(即他的子树dfs序的范围)。以及每次分治过程根节点的信息(就是刚才说的)。细节注意下就可以了。

PS:8.5k的代码也是写的很醉人。

AC代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned __int64
#define eps 1e-8
#define NMAX 30000
#define MOD 998244353
//#define lson l,mid,rt<<1,d
//#define rson mid+1,r,rt<<1|1,d
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
    char c;
    int flag = 0;
    ret=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c == '-')
    {
        flag = 1;
        c = getchar();
    }
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
    if(flag) ret = -ret;
}
const int maxn = 10000+10;

struct Edge
{
    int v,next;
}e[maxn*2];
int head[maxn],ecnt;

void add_edge(int u, int v)
{
    e[ecnt].v = v;
    e[ecnt].next = head[u];
    head[u] = ecnt++;
}

int point;
int Pos[maxn],Size[maxn];

int dp[maxn],mi,pp,sz;

struct Tree
{
    int zero,one,zz,oo,zo;
}Tree[15][maxn];

int ans[15];
bool data[15][maxn],vis[maxn];

void gao1(int u, int fa)
{
    dp[u] = 1;
    sz++;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(v == fa || vis[v]) continue;
        gao1(v,u);
        dp[u] += dp[v];
    }
}

void gao2(int u, int fa)
{
    int tmp = sz-dp[u];
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(v == fa || vis[v]) continue;
        gao2(v,u);
        tmp = max(tmp,dp[v]);
    }
    if(tmp < mi)
    {
        mi = tmp;
        pp = u;
    }
}

int getbarycenter(int u)
{
    sz = 0;
    gao1(u,u);
    mi = 1000000;
    gao2(u,u);
    return pp;
}


struct node
{
    int root, ge, l, r;
    node(){}
    node(int _root, int _ge, int _l, int _r):root(_root),ge(_ge),l(_l),r(_r) {}
};

vector<node>vec[maxn];

bool a[15][maxn];
void solve(int u, int fa, int w, int rt, int tou, int flag)
{
    sz++;
    a[flag][sz] = w;
    int tmp = sz;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(v == fa || vis[v]) continue;
        solve(v,u,w^data[flag][v],rt,tou,flag);
    }
    if(flag == 0) vec[u].push_back(node(rt,tou,tmp,sz));
}

int tot;
int lson[maxn*30],rson[maxn*30];
struct SegmentTree
{
    int zero,one;
    bool flag;
}T[15][maxn*30];

int build(int l, int r)
{
    int rt = ++tot;
    for(int i = 0; i <= 14; i++)
        T[i][rt].flag = 0;
    if(l == r)
    {
//        T[rt].lson = T[rt].rson = 0;
        for(int i = 0; i <= 14; i++)
        {
            T[i][rt].zero = T[i][rt].one = 0;
            if(a[i][l] == 1) T[i][rt].one = 1;
            else T[i][rt].zero = 1;
        }
        return rt;
    }
    int mid = (l+r)>>1;
    lson[rt] = build(l,mid);
    rson[rt] = build(mid+1,r);
    for(int i = 0; i <= 14; i++)
    {
        T[i][rt].one = T[i][lson[rt]].one + T[i][rson[rt]].one;
        T[i][rt].zero = T[i][lson[rt]].zero + T[i][rson[rt]].zero;
    }
    return rt;
}

void dfs(int u)
{
    u = getbarycenter(u);
//        cout<<"tree:"<<u<<" "<<sz<<endl;
    if(sz == 1)
    {
        for(int i = 0; i <= 14; i++)
            Tree[i][u].one = Tree[i][u].zero = Tree[i][u].oo = Tree[i][u].zz = Tree[i][u].zo = 0;
//        vec[u].push_back(node(u,0,0,0));
        return;
    }
    int z[15] = {0}, o[15] = {0}, oo[15] = {0}, zz[15] = {0}, zo[15] = {0};
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(vis[v]) continue;
        for(int j = 0; j <= 14; j++)
        {
            sz = 0;
            solve(v,u,data[j][v],u,point,j);
        }
        int pos = build(1,sz);
//        cout<<"shit1:"<<v<<" "<<sz<<" "<<T[0][pos].one<<endl;
        Pos[point] = pos;
        Size[point++] = sz;
        for(int j = 0; j <= 14; j++)
        {
            oo[j] += T[j][pos].one*o[j];
            zz[j] += T[j][pos].zero*z[j];
            zo[j] += T[j][pos].one*z[j]+T[j][pos].zero*o[j];
            o[j] += T[j][pos].one;
            z[j] += T[j][pos].zero;
        }
    }
    for(int i = 0; i <= 14; i++)
    {
        Tree[i][u].one = o[i];
        Tree[i][u].zero = z[i];
        Tree[i][u].oo = oo[i];
        Tree[i][u].zz = zz[i];
        Tree[i][u].zo = zo[i];
        if(data[i][u] == 1) ans[i] += oo[i]+zz[i]+z[i];
        else ans[i] += zo[i]+o[i];
    }
    vis[u] = 1;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(vis[v]) continue;
        dfs(v);
    }
}

void init(int n)
{
    memset(ans,0,sizeof(ans));
    memset(vis,0,sizeof(vis));
    for(int i = 1; i <= n; i++)
        vec[i].clear();
}

void pushdown(int rt, int flag)
{
    if(T[flag][rt].flag)
    {
        swap(T[flag][lson[rt]].zero,T[flag][lson[rt]].one);
        swap(T[flag][rson[rt]].zero,T[flag][rson[rt]].one);
        T[flag][lson[rt]].flag ^= 1;
        T[flag][rson[rt]].flag ^= 1;
        T[flag][rt].flag = 0;
    }
}

void update(int L, int R, int l, int r, int rt, int flag)
{
    if(L <= l && R >= r)
    {
        T[flag][rt].flag ^= 1;
        swap(T[flag][rt].zero,T[flag][rt].one);
        return;
    }
    pushdown(rt,flag);
    int mid = (l+r)>>1;
    if(L <= mid) update(L,R,l,mid,lson[rt],flag);
    if(R > mid) update(L,R,mid+1,r,rson[rt],flag);
    T[flag][rt].one = T[flag][lson[rt]].one + T[flag][rson[rt]].one;
    T[flag][rt].zero = T[flag][lson[rt]].zero + T[flag][rson[rt]].zero;
}

void rilegou(int x, int flag)
{
    int num = vec[x].size();
    for(int i = 0; i < num; i++)
    {
        int rt = vec[x][i].root,ge = vec[x][i].ge;
        if(data[flag][rt] == 1) ans[flag] -= Tree[flag][rt].zero+Tree[flag][rt].oo+Tree[flag][rt].zz;
        else ans[flag] -= Tree[flag][rt].one+Tree[flag][rt].zo;
        int z = T[flag][Pos[ge]].zero, o = T[flag][Pos[ge]].one;
        Tree[flag][rt].oo -= (Tree[flag][rt].one-o)*o;
        Tree[flag][rt].zz -= (Tree[flag][rt].zero-z)*z;
        Tree[flag][rt].zo -= (Tree[flag][rt].one-o)*z+(Tree[flag][rt].zero-z)*o;
        Tree[flag][rt].one -= o;
        Tree[flag][rt].zero -= z;
        update(vec[x][i].l,vec[x][i].r,1,Size[ge],Pos[ge],flag);
        z = T[flag][Pos[ge]].zero;
        o = T[flag][Pos[ge]].one;
        Tree[flag][rt].oo += o*Tree[flag][rt].one;
        Tree[flag][rt].zz += z*Tree[flag][rt].zero;
        Tree[flag][rt].zo += o*Tree[flag][rt].zero+z*Tree[flag][rt].one;
        Tree[flag][rt].one += o;
        Tree[flag][rt].zero += z;
        if(data[flag][rt] == 1) ans[flag] += Tree[flag][rt].zero+Tree[flag][rt].oo+Tree[flag][rt].zz;
        else ans[flag] += Tree[flag][rt].one+Tree[flag][rt].zo;
    }
    if(data[flag][x] == 1)
    {
        ans[flag] -= Tree[flag][x].zero+Tree[flag][x].oo+Tree[flag][x].zz;
        ans[flag] += Tree[flag][x].one+Tree[flag][x].zo;
        data[flag][x] = 0;
    }
    else
    {
        ans[flag] -= Tree[flag][x].one+Tree[flag][x].zo;
        ans[flag] += Tree[flag][x].zero+Tree[flag][x].oo+Tree[flag][x].zz;
        data[flag][x] = 1;
    }
}

int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o.txt","r",stdin);
#endif
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        memset(head,-1,sizeof(head));
        ecnt = point = tot = 0;
        init(n);
        for(int i = 1; i <= n; i++)
        {
            int tmp;
            scanf("%d",&tmp);
            for(int j = 0; j <= 14; j++)
                data[j][i] = !!(tmp&(1<<j));
        }
        for(int i = 1; i < n; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        dfs(1);
//        cout<<vec[2].size()<<endl;
        ll ret = 0;
        for(int i = 0; i <= 14; i++)
            ret += (ll)ans[i]*(1LL<<i);
//        cout<<ret<<endl;
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            for(int i = 0; i <= 14; i++)
            {
                if(data[i][x] == !!(y&(1<<i))) continue;
                ret -= (ll)ans[i]*(1LL<<i);
                rilegou(x,i);
                ret += (ll)ans[i]*(1LL<<i);
            }
            printf("%I64d\n",ret*2LL);
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值