BZOJ4811: [Ynoi2017]由乃的OJ
Description
Input
Output
Sample Input
1 7
2 6
3 7
3 6
3 1
1 2
2 3
3 4
1 5
1 1 4 7
1 1 3 5
2 1 1 3
2 3 3 3
1 1 3 2
Sample Output
1
5
题解Here!
起床困难综合症上树。。。
建议先把那个签到题$A$了:
BZOJ3668: [Noi2014]起床困难综合症
好麻烦啊。。。
首先一个不变的套路——树链剖分。
我们发现,我们可以用于原题同样的办法来求解。
也即是说,我们用$0$和$(1111111...1)_2$来从前到后取一遍,再从后向前取一遍。
之后再一位一位地贪心。
这样的话复杂度是$O(n\log_2^2n)$的。
然后问题就变成了:如何维护从前到后与从后到前的取值?
带修改,没有可差分性,于是使用线段树。
线段树每个节点维护四个值:
$0$从前到后取一遍的值,$(1111111...1)_2$从前到后取一遍的值;
$0$从后向前取一遍的值,$(1111111...1)_2$从后向前取一遍的值。
假设当前要合并的线段树节点是$l$和$r$,$f_0[l]$的第$i$位代表这一位是用$0$取一遍的值出来的值,$f_1[l]$同理。
那么显然,对于新的合并后节点$u$而言,如果$f_0[u]$的第$i$位(后文中用$f_0[u][i]$表示)是$1$,那么有以下两种情况:
情况一:$f_0[l][i]==1\&\&f_1[r][i]==1$
情况二:$f_0[l][i]==0\&\&f_0[r][i]==1$
可以得到表达式:
$$f_0[u][i]=(f_0[l][i]\&f_1[r][i])|(!f_0[l][i]\&f_0[r][i])$$
那么把这个式子放到整个$f_0[u]$的角度上,就是:
$$f_0[u]=((f_0[l]\&f_1[r])|((!f_0[l])\&f_1[r]))$$
其中$\&,|,\!$分别是按位与,或,取反。
对于$f_1[u]$,也是同样的道理:
$$f_1[u]=((f_1[l]\&f_1[r])|((\!f_1[l])\&f_0[r]))$$
反过来取一遍的同理。
然后我们就可以维护这玩意了。。。
但是代码量。。。
还有,要开$unsigned\ long\ long$。。。
还有,我们求出来两条路径:
$u->LCA(u,v),LCA(u,v)->v$
但是其中一个要把顺序反过来。
然后就没了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define LSON rt<<1 #define RSON rt<<1|1 #define DATAL(x) a[x].data0 #define DATAR(x) a[x].data1 #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r #define MAXN 100010 using namespace std; const unsigned long long MIN=0,MAX=~MIN; int n,m,q,c=1,d=1,top1,top2; int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN]; struct Tree{ int next,to; }tree[MAXN<<1]; struct data{ unsigned long long v,w; friend data operator +(const data &p,const data &q){ data now; now.v=(p.v&q.w)|((~p.v)&q.v); now.w=(p.w&q.w)|((~p.w)&q.v); return now; } }val[MAXN]; struct Segment_Tree{ data data0,data1; int l,r; friend Segment_Tree operator +(const Segment_Tree &p,const Segment_Tree &q){ Segment_Tree now; now.data0=p.data0+q.data0;now.data1=q.data1+p.data1; now.l=p.l;now.r=q.r; return now; } }a[MAXN<<2],ans1[MAXN],ans2[MAXN]; inline long long read(){ long long date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline data calculate(int operate,unsigned long long x){ switch(operate){ case 1:return (data){MIN&x,MAX&x}; case 2:return (data){MIN|x,MAX|x}; case 3:return (data){MIN^x,MAX^x}; } } inline void add_edge(int x,int y){ tree[c].to=y;tree[c].next=head[x];head[x]=c++; tree[c].to=x;tree[c].next=head[y];head[y]=c++; } void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt],will;i;i=tree[i].next){ will=tree[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ id[rt]=d++;pos[id[rt]]=rt;top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt],will;i;i=tree[i].next){ will=tree[i].to; if(will!=fa[rt]&&will!=son[rt])dfs2(will,will); } } inline void pushup(int rt){ DATAL(rt)=DATAL(LSON)+DATAL(RSON); DATAR(rt)=DATAR(RSON)+DATAR(LSON); } void buildtree(int l,int r,int rt){ LSIDE(rt)=l;RSIDE(rt)=r; if(l==r){ DATAL(rt)=DATAR(rt)=val[pos[l]]; return; } int mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int k,data c,int rt){ if(LSIDE(rt)==RSIDE(rt)){ DATAL(rt)=DATAR(rt)=c; return; } int mid=LSIDE(rt)+RSIDE(rt)>>1; if(k<=mid)update(k,c,LSON); else update(k,c,RSON); pushup(rt); } Segment_Tree query(int l,int r,int rt){ if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return a[rt]; int mid=LSIDE(rt)+RSIDE(rt)>>1; if(r<=mid)return query(l,r,LSON); else if(mid<l)return query(l,r,RSON); else return (query(l,mid,LSON)+query(mid+1,r,RSON)); } Segment_Tree query_path(int x,int y){ Segment_Tree ans; top1=top2=0; while(top[x]!=top[y]){ if(deep[top[x]]>=deep[top[y]]){ ans1[++top1]=query(id[top[x]],id[x],1); x=fa[top[x]]; } else{ ans2[++top2]=query(id[top[y]],id[y],1); y=fa[top[y]]; } } if(deep[x]>deep[y])ans1[++top1]=query(id[y],id[x],1); else ans2[++top2]=query(id[x],id[y],1); for(int i=1;i<=top1;i++)swap(ans1[i].data0,ans1[i].data1); if(top1){ ans=ans1[1]; for(int i=2;i<=top1;i++)ans=ans+ans1[i]; if(top2)ans=ans+ans2[top2]; } else ans=ans2[top2]; for(int i=top2-1;i>=1;i--)ans=ans+ans2[i]; return ans; } void solve(int x,int y,unsigned long long z){ unsigned long long ans=0; data t; Segment_Tree s=query_path(x,y); for(int i=63;i>=0;i--){ t.v=(s.data0.v>>i)&1uLL; t.w=(s.data0.w>>i)&1uLL; if(t.v>=t.w||(1uLL<<i)>z)ans|=(t.v?(1uLL<<i):0); else{ ans|=(t.w?(1uLL<<i):0); z-=(1ull<<i); } } printf("%llu\n",ans); } void work(){ int f; unsigned long long x,y,z; while(m--){ f=read();x=read();y=read();z=read(); if(f==1){ solve(x,y,z); } else{ update(id[x],calculate(y,z),1); } } } void init(){ int x,y; unsigned long long k; n=read();m=read();q=read(); for(int i=1;i<=n;i++){ x=read();k=read(); val[i]=calculate(x,k); } for(int i=1;i<n;i++){ x=read();y=read(); add_edge(x,y); } deep[1]=1; dfs1(1); dfs2(1,1); buildtree(1,n,1); } int main(){ init(); work(); return 0; } /* Result: Accepted Time: 3516 ms Because of: Xuan_Xue...... */
$LCT$大法:
这个题能用树剖做,那为什么不能$LCT$呢?
每个节点维护四个值:
两个为中序遍历这棵子树的$f_0,f_1$(分别表示$0$和$MAX$(二进制下全为$1$)跑的答案)
另外两个为中序遍历的反向遍历这棵子树的$f_0,f_1$。
还要记得保存这个点的初始操作。
合并:对于中序遍历的若知道的左边的$f_0,f_1$,右边的$g_0,g_1$,合并后的$h_0,h_1$有:
$$h_0=(\!f_0\&g_0)|(f_0\&g_1)$$
$$h_1=(\!f_1\&g_0)|(f_1\&g_1)$$
反向中序遍历同理。
注意$LCT$丢到$Splay$后翻转操作的修改。
剩下的与NOI2014那道签到题一样,贪心。。。
复杂度为$O(n\log_2n)$。
理论上是能过的。。。但是似乎被卡常了。。。(什么似乎,就是。。。)
所以还是去洛谷开$O2$吧。。。
洛谷P3613 睡觉困难综合征
毒瘤lxl成功地把一道原题出道了$\text{由乃}OI$。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 using namespace std; unsigned long long MIN=0,MAX=~MIN; int n,m,q,c=1; struct node1{ unsigned long long v,w; }val[MAXN]; struct node2{ int f,flag,son[2]; node1 l,r; }a[MAXN]; inline long long read(){ long long date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline long long operation(long long x,long long y,int opt){ if(!opt)return x; switch(opt){ case 1:{x&=y;break;} case 2:{x|=y;break;} case 3:{x^=y;break;} } return x; } inline node1 update(node1 x,node1 y){ node1 rt; rt.v=(~x.v&y.v)|(x.v&y.w); rt.w=(~x.w&y.v)|(x.w&y.w); return rt; } inline bool isroot(int rt){ return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt; } inline void pushup(int rt){ if(!rt)return; a[rt].l=a[rt].r=val[rt]; if(a[rt].son[0]){a[rt].l=update(a[a[rt].son[0]].l,a[rt].l);a[rt].r=update(a[rt].r,a[a[rt].son[0]].r);} if(a[rt].son[1]){a[rt].l=update(a[rt].l,a[a[rt].son[1]].l);a[rt].r=update(a[a[rt].son[1]].r,a[rt].r);} } inline void reserve(int rt){ swap(a[rt].l,a[rt].r);swap(a[rt].son[0],a[rt].son[1]); a[rt].flag^=1; } inline void pushdown(int rt){ if(!rt||!a[rt].flag)return; reserve(a[rt].son[0]);reserve(a[rt].son[1]); a[rt].flag^=1; } inline void turn(int rt){ int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0; if(!isroot(x)){ if(a[y].son[0]==x)a[y].son[0]=rt; else a[y].son[1]=rt; } a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x; a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x; pushup(x);pushup(rt); } void splay(int rt){ int top=0,stack[MAXN]; stack[++top]=rt; for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f; while(top)pushdown(stack[top--]); while(!isroot(rt)){ int x=a[rt].f,y=a[x].f; if(!isroot(x)){ if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt); else turn(x); } turn(rt); } } void access(int rt){ for(int i=0;rt;i=rt,rt=a[rt].f){ splay(rt); a[rt].son[1]=i; pushup(rt); } } inline void makeroot(int rt){access(rt);splay(rt);reserve(rt);} inline void split(int x,int y){makeroot(x);access(y);splay(y);} inline void link(int x,int y){makeroot(x);a[x].f=y;} inline void answer(unsigned long long sum0,unsigned long long sum1,unsigned long long maxn){ unsigned long long ans=0,t=1; for(int i=63;i>=0;i--){ if(sum0&(t<<i))ans+=(t<<i); else if(maxn>=(t<<i)&&(sum1&(t<<i))){ maxn-=(t<<i); ans+=(t<<i); } } printf("%llu\n",ans); } void work1(int x,int y,unsigned long long k){ split(x,y); answer(a[y].l.v,a[y].l.w,k); } void work2(int x,int y,unsigned long long k){ switch(y){ case 1:val[x]=(node1){MIN,k};break; case 2:val[x]=(node1){k,MAX};break; case 3:val[x]=(node1){k,~k};break; } splay(x); } void work(){ int f,x,y; unsigned long long k; while(m--){ f=read();x=read();y=read();k=read(); if(f==1)work1(x,y,k); if(f==2)work2(x,y,k); } } void init(){ int x,y; unsigned long long k; n=read();m=read();q=read(); for(int i=1;i<=n;i++){ x=read();k=read(); switch(x){ case 1:val[i]=(node1){MIN,k};break; case 2:val[i]=(node1){k,MAX};break; case 3:val[i]=(node1){k,~k};break; } } for(int i=1;i<n;i++){ x=read();y=read(); link(x,y); } } int main(){ init(); work(); return 0; } /* Result: Time_Limit_Exceed Because of: chang_shu..... Solution: No_Solution...... */