P5127-子异和【线段树,树链剖分,位运算】
正題
題目鏈接:https://www.luogu.org/problemnew/show/P5127
題目大意
定義一個序列的子異和為所有自己的異或和的和。
然后有點權的樹,要求支持路徑異或和路徑求子異和。
解題思路
首先樹鏈剖分是肯定的,然后我們考慮用哪個數據結構。
從每個位單獨分析,一個長度為nnn的集合,然后cntxcnt_xcntx?表示有多少數的二進制第xxx位為000
然后對于一個位可以貢獻的答案是2cntx?1?2n?cntx=2n?12^{cnt_x-1}*2^{n-cnt_x}=2^{n-1}2cntx??1?2n?cntx?=2n?1,但是若cntx==0cnt_x==0cntx?==0時答案是0。
也就是每個位如果cntx≠0cnt_x\neq 0cntx???=0那么就可以貢獻2x?2n?12^x*2^{n-1}2x?2n?1的答案。
其實我們發現區間或值為sum(or)sum(or)sum(or)那么答案就是2n?1?sum(or)2^{n-1}*sum(or)2n?1?sum(or)。
然后我們區間查詢就可以用線段樹搞了,我們考慮如何區間查詢
我們依舊是每個位進行分析,若第xxx位為0那么保持不變,如果為1那么久等于~sum(and)\sim sum(and)~sum(and)
所以我們可以得出
sum(or)′=(sum(or)&~a)∣(~sum(and)&a)sum(or)'=(sum(or)\& \sim a)|(\sim sum(and)\&a)sum(or)′=(sum(or)&~a)∣(~sum(and)&a)
然后我們多維護一個sum(and)sum(and)sum(and)
sum(and)′=(sum(and)&~a)∣(~sum(or)&a)sum(and)'=(sum(and)\& \sim a)|(\sim sum(or)\&a)sum(and)′=(sum(and)&~a)∣(~sum(or)&a)
圓滿解決該題
codecodecode
#include<cstdio> #include<algorithm> #define ll long long using namespace std; const ll N=201000,XJQ=1e9+7; struct Tree_node{ll l,r,sor,sand,lazy; }; struct Edge_node{ll to,next; }a[N*2]; ll tot,ls[N],n,m,w[N],cnt,pow[N]; ll id[N],siz[N],seg[N],son[N],top[N],dep[N],f[N]; struct Line_Cut_Tree{Tree_node t[N*4];ll ans,Dig;void build(ll x,ll l,ll r){t[x].l=l;t[x].r=r;if(l==r){t[x].sor=t[x].sand=w[id[l]];return;}ll mid=(l+r)/2;build(x*2,l,mid);build(x*2+1,mid+1,r);t[x].sor=t[x*2].sor|t[x*2+1].sor;t[x].sand=t[x*2].sand&t[x*2+1].sand;}void updata(ll x,ll w){ll sor=t[x].sor,sand=t[x].sand;t[x].sor=(sor&(~w))|((~sand)&w);t[x].sand=(sand&(~w))|((~sor)&w);t[x].lazy^=w;}void downdata(ll x){if(!t[x].lazy) return;updata(x*2,t[x].lazy);updata(x*2+1,t[x].lazy);t[x].lazy=0;}ll ask(ll x,ll l,ll r){if(t[x].l==l&&t[x].r==r)return t[x].sor;downdata(x);if(r<=t[x*2].r) return ask(x*2,l,r);if(l>=t[x*2+1].l) return ask(x*2+1,l,r);return ask(x*2,l,t[x*2].r)|ask(x*2+1,t[x*2+1].l,r);t[x].sor=t[x*2].sor|t[x*2+1].sor;t[x].sand=t[x*2].sand&t[x*2+1].sand;}void change(ll x,ll l,ll r,ll val){if(t[x].l==l&&t[x].r==r){updata(x,val);return;}downdata(x);if(r<=t[x*2].r) change(x*2,l,r,val);else if(l>=t[x*2+1].l) change(x*2+1,l,r,val);else change(x*2,l,t[x*2].r,val),change(x*2+1,t[x*2+1].l,r,val);t[x].sor=t[x*2].sor|t[x*2+1].sor;t[x].sand=t[x*2].sand&t[x*2+1].sand;} }Tree; void addl(ll x,ll y) {a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot; } void dfs(ll x,ll fa){siz[x]=1;f[x]=fa;dep[x]=dep[fa]+1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa) continue;dfs(y,x);siz[x]+=siz[y];if(siz[son[x]]<siz[y])son[x]=y;} } void dfs2(ll x,ll fa){seg[x]=++cnt;id[cnt]=x;if(son[x]){top[son[x]]=top[x];dfs2(son[x],x);}for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa||y==son[x]) continue;top[y]=y;dfs2(y,x);} } void push_change(ll x,ll y,ll z){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);Tree.change(1,seg[top[x]],seg[x],z);x=f[top[x]];}if(dep[x]>dep[y]) swap(x,y);Tree.change(1,seg[x],seg[y],z);return; } ll push_ask(ll x,ll y){ll cnt=0,ans=0;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);ans|=Tree.ask(1,seg[top[x]],seg[x]);cnt+=seg[x]-seg[top[x]]+1;x=f[top[x]];}if(dep[x]>dep[y]) swap(x,y);ans|=Tree.ask(1,seg[x],seg[y]);cnt+=seg[y]-seg[x]+1;return ans*pow[cnt-1]%XJQ; } int main() {scanf("%lld%lld",&n,&m);for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}pow[0]=1;for(ll i=1;i<=n;i++)pow[i]=(pow[i-1]*2)%XJQ;for(ll i=1;i<=n;i++)scanf("%lld",&w[i]);dfs(1,0);top[1]=1;dfs2(1,0);Tree.build(1,1,n);for(ll i=1;i<=m;i++){ll ts,a,b,c;scanf("%lld%lld%lld",&ts,&a,&b);if(ts==1)printf("%lld\n",push_ask(a,b));elsescanf("%lld",&c),push_change(a,b,c);} }總結
以上是生活随笔為你收集整理的P5127-子异和【线段树,树链剖分,位运算】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 似水年华歌曲 似水年华歌词
- 下一篇: 赵姬和嫪毐的孩子叫什么 赵姬和嫪毐人物介