[BZOJ]2908: 又是nand
题解: LCT死于卡常....迫不得已写了树剖+线段树 思路很简单 按位分解 对于每一位 考虑这条路径上从右往左第一个0出现的位置 然后判断这一位经过这条路径后是0还是1 统计每一位贡献即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=1e5+10; const double eps=1e-8; #define ll long long using namespace std; struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int sum[MAXN<<2][33]; ll a[MAXN]; int k,n,m,fp[MAXN],p[MAXN]; void up(int x){ inc(i,0,k-1)sum[x][i]=sum[x<<1][i]+sum[x<<1|1][i]; } void built(int x,int l,int r){ if(l==r){ inc(i,0,k-1)sum[x][i]=((a[fp[l]]>>i)&1); return ; } int mid=(l+r)>>1; built(x<<1,l,mid); built(x<<1|1,mid+1,r); up(x); } void update(int x,int l,int r,int t,int key){ if(l==r){ inc(i,0,k-1)sum[x][i]=((key>>i)&1); return ; } int mid=(l+r)>>1; if(t<=mid)update(x<<1,l,mid,t,key); else update(x<<1|1,mid+1,r,t,key); up(x); } int ans[33]; void query1(int x,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr){ inc(i,0,k-1)ans[i]+=sum[x][i]; return ; } int mid=(l+r)>>1; if(ql<=mid)query1(x<<1,l,mid,ql,qr); if(qr>mid)query1(x<<1|1,mid+1,r,ql,qr); } int pos; void query2(int x,int l,int r,int ql,int qr,int id){ if(pos)return ; if(ql<=l&&r<=qr){ if(sum[x][id]==r-l+1){return ;} if(l==r){pos=l;return ;} } int mid=(l+r)>>1; if(qr>mid)query2(x<<1|1,mid+1,r,ql,qr,id); if(ql<=mid)query2(x<<1,l,mid,ql,qr,id); } void query3(int x,int l,int r,int ql,int qr,int id){ if(pos)return ; if(ql<=l&&r<=qr){ if(sum[x][id]==r-l+1){return ;} if(l==r){pos=l;return ;} } int mid=(l+r)>>1; if(ql<=mid)query3(x<<1,l,mid,ql,qr,id); if(qr>mid)query3(x<<1|1,mid+1,r,ql,qr,id); } int fa[MAXN],dep[MAXN],num[MAXN],son[MAXN]; void dfs1(int x,int pre,int deep){ fa[x]=pre;dep[x]=deep+1;num[x]=1; link(x){ if(j->t==pre)continue; dfs1(j->t,x,deep+1); num[x]+=num[j->t]; if(son[x]==-1||num[son[x]]<num[j->t])son[x]=j->t; } } int tp[MAXN],cnt; void dfs2(int x,int td){ tp[x]=td;p[x]=++cnt;fp[p[x]]=x; if(son[x]!=-1)dfs2(son[x],td); link(x)if(j->t!=fa[x]&&j->t!=son[x])dfs2(j->t,j->t); } pii st[MAXN],ed[MAXN];int tot1,tot2; int vis[33]; int Lca(int x,int y){ int xx=tp[x];int yy=tp[y]; while(xx!=yy){ if(dep[xx]<dep[yy])swap(xx,yy),swap(x,y); x=fa[xx];xx=tp[x]; } if(dep[x]>dep[y])swap(x,y); return x; } void solve(int u,int v){ int y=v,x=u; inc(i,0,k-1)vis[i]=0; int uu=tp[u];int vv=tp[v];tot2=tot1=0; while(uu!=vv){ if(dep[uu]>dep[vv]){ed[++tot2]=mp(p[uu],p[u]);u=fa[uu];uu=tp[u];} else {st[++tot1]=mp(p[vv],p[v]);v=fa[vv];vv=tp[v];} } if(dep[u]>dep[v])ed[++tot2]=mp(p[v],p[u]); else st[++tot1]=mp(p[u],p[v]); inc(i,1,tot1){ inc(j,0,k-1)ans[j]=0; query1(1,1,n,st[i].first,st[i].second); inc(j,0,k-1){ if(vis[j])continue; if(ans[j]==st[i].second-st[i].first+1)continue; pos=0; query2(1,1,n,st[i].first,st[i].second,j); vis[j]=pos; } } dec(i,tot2,1){ inc(j,0,k-1)ans[j]=0; query1(1,1,n,ed[i].first,ed[i].second); inc(j,0,k-1){ if(vis[j])continue; if(ans[j]==ed[i].second-ed[i].first+1)continue; pos=0; query3(1,1,n,ed[i].first,ed[i].second,j); vis[j]=pos; } } ll Sum=0; inc(i,0,k-1){ if(vis[i])u=fp[vis[i]]; else u=x; int lca=Lca(u,y); int t=dep[u]+dep[y]-2*dep[lca]+1; if((t&1))Sum+=(1ll<<i); } printf("%lld\n",Sum); } int main(){ n=read();m=read();k=read(); inc(i,1,n)son[i]=-1,a[i]=read(); int u,v; inc(i,2,n)u=read(),v=read(),add(u,v),add(v,u); char str[21]; dfs1(1,0,0);dfs2(1,1); built(1,1,n); while(m--){ scanf("%s",str);u=read(),v=read(); if(str[0]=='R')update(1,1,n,p[u],v); else solve(u,v); } return 0; }
2908: 又是nand
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 557 Solved: 183
[Submit][Status][Discuss]
Description
首先知道A nand B=not(A and B) (运算操作限制了数位位数为K)比如2 nand 3,K=3,则2 nand 3=not (2 and 3)=not 2=5。
给出一棵树,树上每个点都有点权,定义树上从a到b的费用为0与路径上的点的权值顺次nand的结果,例如:从2号点到5号点顺次经过2->3->5,权值分别为5、7、2,K=3,那么最终结果为0 nand 5 nand 7 nand 2=7 nand 7 nand 2=0 nand 2=7,现在这棵树需要支持以下操作。
① Replace a b:将点a(1≤a≤N)的权值改为b。
② Query a b:输出点a到点b的费用。
请众神给出一个程序支持这些操作。
Input
第一行N,M,K,树的节点数量、总操作个数和运算位数。
接下来一行N个数字,依次表示节点i的权值。
接下来N-1行,每行两个数字a,b(1≤a,b≤N)表示a,b间有一条树边。
接下来M行,每行一个操作,为以上2类操作之一。
N、M≤100000,K≤32
Output
对于操作②每个输出一行,如题目所述。
Sample Input
3 3 3
2 7 3
1 2
2 3
Query 2 3
Replace 1 3
Query 1 1
2 7 3
1 2
2 3
Query 2 3
Replace 1 3
Query 1 1
Sample Output
4
7
7