[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 MB
Submit: 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

Sample Output

4
7
posted @ 2019-02-11 19:21  wang9897  阅读(263)  评论(0编辑  收藏  举报