2019 西安邀请赛 BEJ题解

B. Product 点这里传送

E. Tree

题意:有三种操作:分别是树上两点路径上的点or一个数,and一个数和查询两点路径异或和是否等于t。
思路:首先使用树链剖分的线段树进行区间and和区间or,此时复杂度已经nlogn^2,不能再加log了,所以我们来用tag降低复杂度,我们用二进制来表示区间状态,设S=(1<<30)-1,例如:101,表示区间的1和4的个数为奇数个,2的个数为偶数个,设tag_and[o]表示o节点区间需要and的值,初始化为 S,tag_or[o]表示o节点区间需要or的值,初始化为0,假设节点o的区间长度为奇数,那么o节点区间异或和就是 tree[o] & tag_and[o] | tag_or[o],假设为偶数,异或和为tree[o] & tag_and[o] & (S^tag_or[o]),那么我们来试一试传递懒惰标记,假如我要把tag_or[o]传递给儿子son,那么tag_or[son]就改变为tag_or[son] | tag_or[o],tag_and[son] |= (S ^ tag_and[son] &tag_or[son]),tag_and[o]怎么传递呢?交给你来思考了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int a[maxn],id[maxn],n,cnt,S=(1<<30)-1;
int tree[maxn*4],tag_or[maxn*4],tag_and[maxn*4];
int sz[maxn],son[maxn],top[maxn],dep[maxn],f[maxn],rk[maxn];
vector<int>G[maxn];
void dfs1(int u,int fa,int deep)
{
    f[u]=fa;
    dep[u]=deep;
    sz[u]=1;
    for(auto v : G[u]) {
        if(v==fa)
            continue;
        dfs1(v,u,deep+1);
        sz[u]+=sz[v];
        if(sz[son[u]]<sz[v])
            son[u]=v;
    }
}
void dfs2(int u,int rt)
{
    id[u]=++cnt;
    rk[cnt]=a[u];
    top[u]=rt;
    if(son[u])
        dfs2(son[u],rt);
    for(auto v : G[u]) {
        if(v==f[u]||v==son[u])
            continue;
        dfs2(v,v);
    }
}
void build(int o,int l,int r)
{
    tag_and[o]=S;
    tag_or[o]=0;
    if(l==r)
    {
        tree[o]=rk[l];
        return;
    }
    int m=(l+r)/2,ls=o*2,rs=o*2+1;
    build(ls,l,m);
    build(rs,m+1,r);
    tree[o]=tree[ls]^tree[rs];
}
void calc(int o,int l,int r)
{
    tree[o]&=tag_and[o];
    if((r-l+1)%2)
        tree[o]|=tag_or[o];
    else
        tree[o]&=(S^tag_or[o]);
}
void AddTag(int o,int v,int op)
{
    if(op==1)
    {
        tag_or[o]|=v;
        int s=(S^tag_and[o]);
        s&=tag_or[o];
        tag_and[o]|=s;
    }
    else
    {
        tag_and[o]&=v;
        tag_or[o]&=tag_and[o];
    }
}
void pushdown(int o,int ls,int rs,int l,int m,int r)
{
    if(tag_or[o]==0&&tag_and[o]==S)
        return;
    if(tag_or[o]!=0)
    {
        AddTag(ls,tag_or[o],1);
        AddTag(rs,tag_or[o],1);
        tag_or[o]=0;
    }
    if(tag_and[o]!=S)
    {
        AddTag(ls,tag_and[o],2);
        AddTag(rs,tag_and[o],2);
        tag_and[o]=S;
    }
    calc(ls,l,m);
    calc(rs,m+1,r);
}
void up(int o,int l,int r,int ql,int qr,int v,int op)
{
    if(l>=ql&&r<=qr)
    {
        AddTag(o,v,op);
        calc(o,l,r);
        return;
    }
    int m=(l+r)/2,ls=o*2,rs=o*2+1;
    pushdown(o,ls,rs,l,m,r);
    if(ql<=m)
        up(ls,l,m,ql,qr,v,op);
    if(qr>m)
        up(rs,m+1,r,ql,qr,v,op);
    tree[o]=tree[ls]^tree[rs];
}
int qu(int o,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)
        return tree[o];
    int m=(l+r)/2,ls=o*2,rs=o*2+1,res=0;
    pushdown(o,ls,rs,l,m,r);
    if(ql<=m)
        res^=qu(ls,l,m,ql,qr);
    if(qr>m)
        res^=qu(rs,m+1,r,ql,qr);
    return res;
}
int up_path(int x,int y,int v,int op)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
            swap(x,y);
        up(1,1,n,id[top[x]],id[x],v,op);
        x=f[top[x]];
    }
    if(id[x]>id[y])
        swap(x,y);
    up(1,1,n,id[x],id[y],v,op);
}
int qu_path(int x,int y)
{
    int res=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
            swap(x,y);
        res^=qu(1,1,n,id[top[x]],id[x]);
        x=f[top[x]];
    }
    if(id[x]>id[y])
        swap(x,y);
    res^=qu(1,1,n,id[x],id[y]);
    return res;
}
int main()
{
    int m,u,v,op,s,t;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    while(m--)
    {
        scanf("%d%d%d",&op,&s,&t);
        if(op==1)
            up_path(1,s,t,1);
        else if(op==2)
            up_path(1,s,t,2);
        else
        {
            int x=qu_path(1,s);
            if(x!=t)
                puts("YES");
            else
                puts("NO");
        }
    }
}

J. And And And

思路:我们统计每一个点作为端点的贡献,我们对树进行dfs,假设1为根,sz[u]为u子树大小,记mp[w]为当前dfs序走过的点中,权值为w的点的size总和,这个size有双重含义,假设当前走到的点为u,假设v的权值为w,如果v为u的祖先,那么size[v]=n-sz[son[v]],如果v不为u的祖先,那么size[v]=sz[v],那么点u作为端点的贡献就是:sz[u]*mp[w[u]]。
#include<bits/stdc++.h>
#define ll long long
#define pi pair<int,ll>
#define mk make_pair
using namespace std;
const int maxn=1e5+10,mod=1e9+7;
unordered_map<ll,int>mp;
vector<pi>G[maxn];
ll w[maxn];
int sz[maxn],n,ans;
void dfs1(int u)
{
    sz[u]=1;
    for(auto tmp : G[u]) {
        w[tmp.first]=w[u]^tmp.second;
        dfs1(tmp.first);
        sz[u]+=sz[tmp.first];
    }
}
void dfs2(int u)
{
    ans=(ans+1ll*sz[u]*mp[w[u]]%mod)%mod;
    for (auto tmp : G[u]) {
        int v=tmp.first;
        mp[w[u]]=(mp[w[u]]+n-sz[v])%mod;
        dfs2(v);
        mp[w[u]]=(mp[w[u]]-n+sz[v]+mod)%mod;
    }
    mp[w[u]]=(mp[w[u]]+sz[u])%mod;
}
int main()
{
    int u,v;
    ll W;
    cin>>n;
    for(v=2;v<=n;v++)
    {
        cin>>u>>W;
        G[u].push_back(mk(v,W));
    }
    dfs1(1);
    dfs2(1);
    cout<<ans;
}

发布了302 篇原创文章 · 获赞 98 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/90607753
今日推荐