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;
}