zoukankan      html  css  js  c++  java
  • 洛谷 P6072 『MdOI R1』Path(回滚莫队+01trie)

    题面传送门

    又是 ix35 神仙出的题,先以 mol 为敬 %%%

    首先预处理出根节点到每个点路径上权值的异或和 \(dis_i\),那么两点 \(a,b\) 路径上权值的异或和显然为 \(dis_a\oplus dis_b\)

    我们考虑探究 \(a,b\)\(c,d\) 间的路径不相交意味着什么。记 \(l=lca(a,b)\),显然 \(c,d\) 不能一个在 \(l\) 子树内,一个在 \(l\) 子树外,否则它们间的路径就会经过 \(l\) 了。那么分两种情况,\(c,d\) 全在 \(l\) 子树外,和 \(c,d\) 全在 \(l\) 子树内。\(c,d\) 全在子树外的情况显然好搞定,只要 \(c,d\) 都在 \(l\) 的子树外,那么 \(a,b\)\(c,d\) 之间的路径就肯定不会相交。比较麻烦的是 \(c,d\) 全在 \(l\) 子树内的情况,记 \(l'=lca(c,d)\),显然 \(l'\neq l\),而 \(c,d\) 都在 \(l\) 子树内,故 \(l'\) 也在 \(l\) 子树内,如果我们交换 \((a,b)\)\((c,d)\),那么可得 \(c,d\) 的 lca 在 \(a,b\) 的 lca \(l'\) 的子树外,故第二种情况可以规约到第一种情况。所以我们只用考虑第一种情况就行了。

    考虑枚举 \(a,b\) 的 lca \(l\),如果我们按照 DFS 序将原树展开成一个序列,那么相当于在 \([dfn_l,dfn_l+sz_l-1]\)\([1,dfn_l-1]\cup[dfn_l+sz_l,n]\) 中分别选择两个数 \(a,b\)\(c,d\) 使得 \(dis_a\oplus dis_b+dis_c\oplus dis_d\) 最大。那么我们只用让它们分别最大即可。而如果我们令 \(dfn_i=dfn_{i-n}(i>n)\),那么后面那个区间并又可写成 \([dfn_l+sz_l,dfn_l+n-1]\)。于是现在题目转化为:给定一个序列 \(a\),要求在 \([l,r]\) 中选择两个数 \(a_i,a_j\)\(a_i\oplus a_j\) 的最大值。首先可以肯定的是这东西没法用 DS 直接维护,而本题 3e4 的数据范围也在疯狂暗示本题的根号算法。故考虑莫队,建立一个 01-trie,插入某个数 \(x\) 的时候就按照套路将其插入 01-trie,而本题的答案以取 \(\max\) 出现的,不支持删除。故使用回滚莫队,扫到右端点的时候记录一个 \(tmp\) 保存答案,解决一个询问之后就用临时保存的值还原答案即可。

    时间复杂度 \(n\sqrt{n}\log w\)

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    template<typename T> void read(T &x){
    	x=0;char c=getchar();T neg=1;
    	while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	x*=neg;
    }
    const int MAXN=3e4;
    const int SQRT=245;
    const int LOG_N=30;
    const int MAXP=1e6;
    int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],cst[MAXN*2+5],ec=0;
    void adde(int u,int v,int w){to[++ec]=v;cst[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
    int dis[MAXN+5],dfn[MAXN+5],ed[MAXN+5],id[MAXN+5],tim=0;
    void dfs(int x,int f){
    	dfn[x]=++tim;id[tim]=x;
    	for(int e=hd[x];e;e=nxt[e]){
    		int y=to[e],z=cst[e];if(y==f) continue;
    		dis[y]=dis[x]^z;dfs(y,x);
    	} ed[x]=tim;
    }
    int blk_sz,blk_cnt,L[SQRT+5],R[SQRT+5],bel[MAXN*2+5];
    int w[MAXN*2+5];
    struct query{
    	int l,r,id;
    	bool operator <(const query &rhs){
    		if(bel[l]!=bel[rhs.l]) return l<rhs.l;
    		return r<rhs.r;
    	}
    } q[MAXN*2+5];
    int ch[MAXP+5][2],siz[MAXP+5],ncnt=0;
    void insert(int x,int v){
    	int cur=0;
    	for(int i=LOG_N;~i;i--){
    		int d=x>>i&1;
    		if(!ch[cur][d]) ch[cur][d]=++ncnt;
    		cur=ch[cur][d];siz[cur]+=v;
    	}
    }
    int query(int v){
    	int x=0,cur=0;
    	for(int i=LOG_N;~i;i--){
    		int d=v>>i&1;
    		if(siz[ch[cur][d^1]]) x|=1<<i,cur=ch[cur][d^1];
    		else cur=ch[cur][d];
    	} return x;
    }
    int ans=0,res[MAXN+5];
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<n;i++){
    		int u,v,w;scanf("%d%d%d",&u,&v,&w);
    		adde(u,v,w);adde(v,u,w);
    	} dfs(1,0);
    	for(int i=1;i<=n;i++) w[i]=dis[id[i]];
    	for(int i=n+1;i<=n*2;i++) w[i]=w[i-n];
    //	for(int i=1;i<=n*2;i++) printf("%d\n",w[i]);
    	blk_sz=(int)sqrt(2*n);blk_cnt=(2*n-1)/blk_sz+1;
    	for(int i=1;i<=blk_cnt;i++){
    		L[i]=(i-1)*blk_sz+1;
    		R[i]=min(i*blk_sz,2*n);
    		for(int j=L[i];j<=R[i];j++) bel[j]=i;
    	}
    	for(int i=2;i<=n;i++){
    		q[i-1].l=dfn[i];q[i-1].r=ed[i];q[i-1].id=i;
    		q[i+n-2].l=ed[i]+1;q[i+n-2].r=dfn[i]+n-1;q[i+n-2].id=i;
    //		printf("%d %d %d\n",dfn[i],ed[i],i);
    //		printf("%d %d %d\n",ed[i]+1,dfn[i]+n-1,i); 
    	} sort(q+1,q+(n<<1)-1);int cl=1,cr=0;
    //	for(int i=1;i<=(n<<1)-2;i++) printf("%d %d %d\n",q[i].l,q[i].r,q[i].id);
    	for(int i=1;i<=(n<<1)-2;i++){
    		if(i==1||bel[q[i].l]!=bel[q[i-1].l]){
    			cl=R[bel[q[i].l]]+1;cr=cl-1;
    			memset(siz,0,sizeof(siz));memset(ch,0,sizeof(ch));
    			ncnt=0;ans=0;
    		}
    		if(bel[q[i].l]==bel[q[i].r]){
    			int mx=-0x3f3f3f3f;
    			for(int j=q[i].l;j<=q[i].r;j++) insert(w[j],1);
    			for(int j=q[i].l;j<=q[i].r;j++) mx=max(mx,query(w[j]));
    			for(int j=q[i].l;j<=q[i].r;j++) insert(w[j],-1);
    			res[q[i].id]+=mx;continue;
    		}
    		while(cr<q[i].r) insert(w[++cr],1),ans=max(ans,query(w[cr]));
    		int tmp=ans;
    		while(cl>q[i].l) insert(w[--cl],1),ans=max(ans,query(w[cl]));
    		res[q[i].id]+=ans;
    		while(cl<R[bel[q[i].l]]+1) insert(w[cl++],-1);
    		ans=tmp;
    	} int mx=0;
    	for(int i=1;i<=n;i++) chkmax(mx,res[i]);
    	printf("%d\n",mx);
    	return 0;
    }
    
  • 相关阅读:
    RSA加密算法
    ios 经典错误
    C--指针函数,static
    svn---命令行操作
    iOS中的自由桥接
    ios--socket
    ios错误修改了系统头文件
    ios数据库FMDB
    CoreDate的使用
    ios简单数据库运用
  • 原文地址:https://www.cnblogs.com/ET2006/p/P6072.html
Copyright © 2011-2022 走看看