1. 程式人生 > >BZOJ4811: [Ynoi2017]由乃的OJ BZOJ3668: [Noi2014]起床困難綜合症

BZOJ4811: [Ynoi2017]由乃的OJ BZOJ3668: [Noi2014]起床困難綜合症

BZOJ4811: [Ynoi2017]由乃的OJ

Description

由乃正在做她的OJ。現在她在處理OJ上的使用者排名問題。OJ上註冊了n個使用者,編號為1~",一開始他們按照編號排名。 由乃會按照心情對這些使用者做以下四種操作,修改使用者的排名和編號: 然而由乃心情非常不好,因為Deus天天問她題。。。 因為Deus天天問由乃OI題,所以由乃去學習了一下OI,由於由乃智商挺高,所以OI學的特別熟練她在RBOI2016中以第一名的成績進入省隊,參加了NOI2016獲得了金牌保送 Deus:這個題怎麼做呀? yuno:這個不是NOI2014的水題嗎。。。 Deus:那如果出到樹上,多組鏈詢問,帶修改呢? yuno:誒。。。??? Deus:這題叫做睡覺困難綜合徵喲~ 雖然由乃OI很好,但是她基本上不會DS,線段樹都只會口胡,比如她NOI2016的分數就是100+100+100+0+100+100。。。NOIP2017的分數是100+0+100+100+0+100 所以她還是隻能找你幫她做了。。。 給你一個有n個點的樹,每個點的包括一個位運算opt和一個權值x,位運算有&,l,^三種,分別用1,2,3表示。 每次詢問包含三個數x,y,z,初始選定一個數v。 然後v依次經過從x到y的所有節點,每經過一個點i,v就變成v opti xi,所以他想問你,最後到y時,希望得到的值儘可能大,求最大值? 給定的初始值v必須是在[0,z]之間。 每次修改包含三個數x,y,z,意思是把x點的操作修改為y,數值改為z

Input

第一行三個數n,m,k。k的意義是每個點上的數,以及詢問中的數值z都 <2^k。 之後n行每行兩個數x,y表示該點的位運算編號以及數值 之後n - 1行,每行兩個數x,y表示x和y之間有邊相連 之後m行,每行四個數,Q,x,y,z表示這次操作為Q(1位詢問,2為修改),x,y,z意義如題所述 0 <= n , m <= 100000 , k <= 64

Output

對於每個操作1,輸出到最後可以造成的最大刺激度v

Sample Input

5 5 3
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

7
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......

*/