您的位置:首页 > 其它

[bzoj4811] [Ynoi2017]由乃的OJ

2017-09-14 18:49 423 查看

题目大意

给定一棵n个节点的树,每个节点上有一个位运算操作(&,|,^)和一个数。

m次操作:1. 修改一个点的操作和数 2. 给定x,y,v,在[0,v]中找一个整数,在树上从x跑到y,到一个节点当前的数对该节点的操作和数进行位运算。问最大可能得到什么数。

n,m≤100000 数的范围小于264

分析

考虑如何做询问操作。显然可以从高到低逐位确定。现在问题是要求每一位填0或1后从x跑到y,会变成什么。

首先可以想到树链剖分,其次区间答案可合并且不同位不相互影响,所以可以树链剖分+线段树求答案。

但是二进制有64位啊~只要用两个64位整数,分别存64位全是0和全是1的答案,合并时用位运算可以做到O(1)

总时间复杂度就是O(nlog2n)的

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=1e5+5,M=262200,L=17;

typedef unsigned long long LL;

int n,m,sb,rt,opt
,dfn
,Top
,tot,h
,e[N<<1],nxt[N<<1],ch
,Size
,fa
[L],dep
;

LL v
;

struct Data
{
LL s0,s1;
}T1[M],T2[M],ans;

Data operator + (Data a,Data b)
{
Data c;
c.s0=((a.s0^(-1))&b.s0)|(a.s0&b.s1);
c.s1=((a.s1^(-1))&b.s0)|(a.s1&b.s1);
return c;
}

char c;

LL read()
{
LL x=0; int sig=1;
for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x*sig;
}

void add(int x,int y)
{
e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}

void Init(int x)
{
dep[x]=dep[fa[x][0]]+1;
for (int i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0])
{
fa[e[i]][0]=x; Init(e[i]); Size[x]=Size[e[i]]+1;
}
}

void Change(int l,int r,int g,int x)
{
if (l==r)
{
if (opt[ch[g]]==1) T1[x].s0=0,T1[x].s1=v[ch[g]];
else if (opt[ch[g]]==2) T1[x].s0=v[ch[g]],T1[x].s1=-1;
else T1[x].s0=v[ch[g]],T1[x].s1=(-1)^v[ch[g]];
T2[x]=T1[x];
return;
}
int mid=l+r>>1;
if (g<=mid) Change(l,mid,g,x<<1);else Change(mid+1,r,g,x<<1|1);
T1[x]=T1[x<<1]+T1[x<<1|1];
T2[x]=T2[x<<1|1]+T2[x<<1];
}

void dfs(int x)
{
dfn[x]=++tot; ch[tot]=x; Top[tot]=rt;
Change(1,n,tot,1);
int i,j=0;
for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0] && (!j || Size[e[i]]>Size[j])) j=e[i];
if (!j) return;
dfs(j);
for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x][0] && e[i]!=j)
{
rt=tot+1;
dfs(e[i]);
}
}

int getlca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=L-1;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for (int i=L-1;i>=0;i--) if (fa[x][i]!=fa[y][i])
{
x=fa[x][i]; y=fa[y][i];
}
return fa[x][0];
}

void ask1(int l,int r,int a,int b,int x)
{
if (l==a && r==b)
{
ans=ans+T1[x]; return;
}
int mid=l+r>>1;
if (b<=mid) ask1(l,mid,a,b,x<<1);
else if (a>mid) ask1(mid+1,r,a,b,x<<1|1);
else
{
ask1(l,mid,a,mid,x<<1); ask1(mid+1,r,mid+1,b,x<<1|1);
}
}

void go1(int x,int y)
{
if (x==y) return;
x=dfn[x];
int t=Top[x];
if (t<=dfn[y]) t=dfn[y]+1;
go1(fa[ch[t]][0],y);
ask1(1,n,t,x,1);
}

void ask2(int l,int r,int a,int b,int x)
{
if (l==a && r==b)
{
ans=ans+T2[x]; return;
}
int mid=l+r>>1;
if (b<=mid) ask2(l,mid,a,b,x<<1);
else if (a>mid) ask2(mid+1,r,a,b,x<<1|1);
else
{
ask2(mid+1,r,mid+1,b,x<<1|1); ask2(l,mid,a,mid,x<<1);
}
}

void go2(int x,int y)
{
if (x==y) return;
x=dfn[x];
int t=Top[x];
if (t<=dfn[y]) t=dfn[y]+1;
ask2(1,n,t,x,1);
go2(fa[ch[t]][0],y);
}

int main()
{
n=read(); m=read(); sb=read();
for (int i=1;i<=n;i++) opt[i]=read(),v[i]=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y); add(y,x);
}
Init(1); rt=1; tot=0;
dfs(1);
for (int j=1;j<L;j++) for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
for (;m--;)
{
LL typ=read(),x=read(),y=read(),z=read();
if (typ==1)
{
int lca=getlca(x,y);
ans.s0=0; ans.s1=-1;
go2(x,fa[lca][0]); go1(y,lca);
LL s=0; bool bz=1;
for (int i=sb-1;i>=0;i--)
{
LL st=(LL)1<<i;
if ((st&z)==0 && bz) s|=(st&ans.s0);else
{
if ((st&ans.s0)>=(st&ans.s1))
{
s|=(st&ans.s0); bz=0;
}else s|=(st&ans.s1);
}
}
printf("%llu\n",s);
}else
{
opt[x]=y; v[x]=z;
Change(1,n,dfn[x],1);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: