• WC2018 通道 与 CTSC2018 暴力写挂


    https://www.luogu.com.cn/problem/P4220
    https://www.luogu.com.cn/problem/P4565

    两个都给出点分治的做法,看起来边分治不光跑的慢还没什么不可替代性?

    暴力写挂
    考虑那个式子有两个不同树上的 \(\operatorname{LCA}\),不好处理,考虑怎么换成一个
    由于 \(dis(x,y)=deep(x)+deep(y)-2deep(\operatorname{LCA}(x,y))\),于是用 \(dis\) 代换:\(\dfrac{1}{2}(dis(x,y)+deep(x)+deep(y)-2deep'(\operatorname{LCA'}(x,y)))\),后面省略那个 \(\frac{1}{2}\)
    可以先对第一个树进行点分治,设当前的分治中心是 \(u\),那么式子就变成了 \(dis(u,x)+dis(u,y)+deep(x)+deep(y)-2deep'(\operatorname{LCA'}(x,y))\)
    \(w(x)=dis(u,x)+deep(x)\),这个可以一遍 dfs 求出
    那么要求的东西最终就变成了 \(w(x)+w(y)-2deep'(\operatorname{LCA'}(x,y))\),这个还要求 \(x,y\) 来源于 \(u\) 的不同的子树
    可以把当前要分治的联通块拿出来,在第二课树上建立虚树,虚树上 dp,设 \(f(u,0)\) 表示 \(u\) 子树内 \(w(x)\) 最大的点,\(f(u,1)\) 表示次大、且满足和最大点在第一棵树中不属于同一子树 的点
    转移简单,在 \(\operatorname{LCA}\) 处更新答案即可

    对于原本不在当前要分治的联通块,但是因为结构需要被加入虚树的点,可以以他为 \(\operatorname{LCA}\) 更新答案,但是不能用他的 \(w\) 初始化 \(f(u,0)\)
    st 表求 \(\operatorname{LCA}\) 加上基数排序实现线性建虚树可以做到 \(O(n\log n)\)

    #define N 400006
    #define M 800006
    #define LOG_N 20
    struct Graph{
    	int fir[N],nex[M],to[M],w[M],tot;
    	inline void add(int u,int v,int c=0,int flag=1){
    		to[++tot]=v;nex[tot]=fir[u];fir[u]=tot;w[tot]=c;
    		if(flag) add(v,u,c,0);
    	}
    };
    int n;
    Graph T;
    int st[LOG_N][N*2],lg2[N*2];
    int dfscnt,dfn[N],id[N*2];
    int deep[N];
    long long sumT[N];
    void dfsT(int u,int fa=0){
    	deep[u]=deep[fa]+1;dfn[u]=++dfscnt;id[dfscnt]=u;
    	for(int v,i=T.fir[u];i;i=T.nex[i]){
    		v=T.to[i];
    		if(v==fa) continue;
    		sumT[v]=sumT[u]+T.w[i];
    		dfsT(v,u);id[++dfscnt]=u;
    	}
    }
    inline int getLca(int u,int v){
    	if(dfn[u]>dfn[v]) lib::swap(u,v);
    	u=dfn[u];v=dfn[v];
    	int len=lg2[v-u+1];
    	return deep[st[len][u]]<deep[st[len][v-(1<<len)+1]]?st[len][u]:st[len][v-(1<<len)+1];
    }
    //inline long long getDis(int u,int v){return sumT[u]+sumT[v]-(sumT[getLca(u,v)]<<1);}
    inline void initT(){//pre for lca on T
    	for(int u,v,i=1;i<n;i++) u=read(),v=read(),T.add(u,v,reads());
    	dfsT(1);st[0][1]=id[1];
    	for(int i=2;i<=dfscnt;i++) lg2[i]=lg2[i>>1]+1,st[0][i]=id[i];
    	for(int j=1;j<LOG_N;j++)for(int i=1;i+(1<<j)<=dfscnt;i++)
    		st[j][i]=deep[st[j-1][i]]<deep[st[j-1][i+(1<<(j-1))]]?st[j-1][i]:st[j-1][i+(1<<(j-1))];
    }
    #define SORT_MAX 80
    #define MAX_DIGIT 3
    #define BASE 256
    __attribute__((always_inline))inline int cmpDfn(const int &a,const int &b){return dfn[a]<dfn[b];}
    inline void sort(int n,int *a){
    	if(n<=SORT_MAX) return std::sort(a+1,a+1+n,cmpDfn),void();
    	static int cnt[BASE],tmp[N];
    	for(int x=1,i=0;i<MAX_DIGIT;i++,x*=BASE){
    		for(int j=1;j<=n;j++) cnt[(dfn[a[j]]/x)%BASE]++;
    		for(int j=1;j<BASE;j++) cnt[j]+=cnt[j-1];
    		for(int j=n;j;j--) tmp[cnt[(dfn[a[j]]/x)%BASE]--]=a[j];
    		__builtin_memset(cnt,0,sizeof cnt);
    		__builtin_memcpy(a,tmp,(n+1)*sizeof a[0]);
    	}
    }
    Graph H;
    void build(int n,int *a){
    	sort(n,a);
    	static int stack[N],top;
    	top=1;stack[1]=1;H.tot=H.fir[1]=0;
    	for(int lca,i=1;i<=n;i++)if(a[i]^1){
    		lca=getLca(a[i],stack[top]);
    		if(lca^stack[top]){
    			while(dfn[lca]<dfn[stack[top-1]]) H.add(stack[top-1],stack[top]),top--;
    			if(lca^stack[top-1]) H.fir[lca]=0,H.add(lca,stack[top]),stack[top]=lca;
    			else H.add(lca,stack[top--]);
    		}
    		H.fir[a[i]]=0;stack[++top]=a[i];
    	}
    	while(--top) H.add(stack[top],stack[top+1]);
    }
    int col[N];
    long long w[N];
    struct Ans{
    	int col;
    	long long ans;
    	__attribute__((always_inline))inline int operator < (const Ans &b)const{return ans>b.ans;}
    };
    inline void update(Ans &u0,Ans &u1,const Ans &max,const Ans &max2){
    	static Ans id[4];
    	id[0]=u0;id[1]=u1;id[2]=max;id[3]=max2;
    	std::sort(id,id+4);
    	u0=id[0];
    	for(int j=1;;j++)if(id[j].col^id[0].col){
    		u1=id[j];break;
    	}
    }
    int real[N],realId;//is vertex u really exist?
    Ans f[N][2];
    long long ans;
    void dp(int u,int fa=0){
    	f[u][0]=f[u][1]=(Ans){-1,-_LL_INF};
    	if(real[u]==realId) f[u][0]=(Ans){col[u],w[u]};
    	for(int v,i=H.fir[u];i;i=H.nex[i]){
    		v=H.to[i];
    		if(v==fa) continue;
    		dp(v,u);
    		lib::chkMax(ans,f[u][0].ans+(f[u][0].col==f[v][0].col?f[v][1].ans:f[v][0].ans)-(sumT[u]<<1));
    		lib::chkMax(ans,f[u][1].ans+(f[u][1].col==f[v][0].col?f[v][1].ans:f[v][0].ans)-(sumT[u]<<1));
    		update(f[u][0],f[u][1],f[v][0],f[v][1]);
    	}
    }
    Graph G;
    long long sumG[N];
    void pre(int u,int fa=0){
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa) continue;
    		sumG[v]=sumG[u]+G.w[i];pre(v,u);
    	}
    }
    int root,maxson[N],vis[N],size[N];
    void dfs(int u,int color,int *a,int fa=0){
    	real[u]=realId;
    	col[u]=color;a[++a[0]]=u;
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa||vis[v]) continue;
    		w[v]=w[u]+G.w[i];dfs(v,color,a,u);
    	}
    	w[u]+=sumG[u];
    }
    inline void calc(int u){//root: u
    	static int a[N];
    	a[0]=col[0]=0;
    	a[++a[0]]=u;col[u]=++col[0];w[u]=0;
    	real[u]=++realId;
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(vis[v]) continue;
    		col[0]++;w[v]=w[u]+G.w[i];dfs(v,col[0],a);
    	}
    	w[u]+=sumG[u];
    	build(a[0],a);
    	dp(1);
    }
    void findRoot(int u,int fa=0){
    	size[u]=1;maxson[u]=0;
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa||vis[v]) continue;
    		findRoot(v,u);size[u]+=size[v];
    		lib::chkMax(maxson[u],size[v]);
    	}
    	lib::chkMax(maxson[u],size[0]-size[u]);
    	if(maxson[u]<maxson[root]) root=u;
    }
    void divide(int u,int tot){
    	vis[u]=1;calc(u);
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(vis[v]) continue;
    		root=0;size[0]=size[v]>size[u]?(tot-size[u]):size[v];
    		findRoot(v);divide(root,size[v]);
    	}
    }
    int main(){
    		#ifdef LOCAL
    		freopen("wronganswer20.in","r",stdin);
    		#endif
    	n=read();
    	for(int u,v,i=1;i<n;i++) u=read(),v=read(),G.add(u,v,reads());
    	pre(1);
    	initT();
    	ans=-_LL_INF;
    	root=0;maxson[0]=_INT_INF;size[0]=n;
    	findRoot(1);divide(root,size[1]);
    	ans>>=1;
    	for(int i=1;i<=n;i++) lib::chkMax(ans,sumG[i]-sumT[i]);
    	writeEN(ans);
    	return RET;
    }
    

    通道
    和上一个一样,还是想办法通过枚举一些东西来减少变化的量,尽量让变化的东西都在一个树上,尤其是减少式子中在多个树上求 \(\operatorname{LCA}\) 的情况
    考虑在第一个树上点分治,分治中心是 \(u\),然后在第二个树上枚举 \(p\) 表示这两个点 \(x,y\) 在第二个树上的 \(\operatorname{LCA}\)
    式子变成了:\(dis_1(x,u)+dis_1(y,u)+deep_2(x)+deep_2(y)-2deep_2(p)+dis_3(x,y)\),需要保证 \(x,y\) 在第一、第二棵树上都来自不同的子树
    对第二课建立虚树,在第二课树上来自的子树不同很好保证,只需要注意一下啊更新答案、合并 dp 数组的顺序即可
    发现如果要满足在第一颗树上来自 \(u\) 的不同子树,那么很难设计出这个 dp,考虑每次只合并 \(u\)两个子树

    可以证明:若给树分成两类点,那么分别求出两端点都是同一类点的两条直径,会得到四个端点,那么两端点不是同一类点的直径的端点,一定在这四个点中

    据此,维护 \(f(u,0/1)\) 表示第二课树的子树 \(u\) 内的所有点,在第三课树上组成的直径(两端点都是第一、第二类点)最长是多少、端点是啥
    更新答案的时候,就可以对于每个孩子 \(v\),把他们的端点稍微组合组合就行了
    \(f(v,0/1)\) 合并到 \(f(u,0/1)\) 的时候,仍然应用上面的性质,就把 \(v\) 里面的点算作一类点,剩下的算作另一类,拿出端点来组合一下

    至此解决了每次合并 \(u\)(分治中心)的两棵子树的问题,发现边分治的话因为是每次找中心边,一共只有两个子树,已经做完了。但是想要一个点分治的做法
    我们把 \(u\) 的所有子树按照大小排序,按照合并果子的方式来合并他们(每次合并两个),最后再把所有子树和 \(u\) 合并一次
    这样做如果每次合并的复杂度是 \(O(size_a+size_b)\) 的,那么点分治的总复杂度仍然是 \(O(n\log n)\)

    简略证明,对合并的过程建哈夫曼树,那么大小为 \(s\) 的联通块在哈夫曼树上的深度是 \(\log \frac{s}{n}\),于是有:

    \[T(u)=\sum_{u\rightarrow v}T(v)+\sum_{u\rightarrow v} size_v\log \frac{size_v}{n} \]

    \[T(u)=\sum_{u\rightarrow v}T(v)+\sum_{u\rightarrow v} size_v\log n-\sum_{u\rightarrow v} size_v\log size_v \]

    \[T(u)=size_u\log n+\sum_{u\rightarrow v}T(v)-\sum_{u\rightarrow v} size_v\log size_v \]

    然后你发现这样加上去中间的项都消了,最后就剩一个 \(O(n\log n)\)

    线性建虚树,并精细实现合并果子的过程:先基数排序,然后用两个队列维护。可以做到 \(O(n\log n)\)
    但懒得这么写了,写的 \(O(n\log^2 n)\) 已经排进了洛谷最优解第一页。。。感觉要是再去个 \(\log\) 就最优解了(

    #define N 100006
    #define M 200006
    #define LOG_N 18
    int lg2[N*2];
    struct Graph{
    	int fir[N],nex[M],to[M],tot;
    	long long w[M];
    	inline void add(int u,int v,long long c,int flag=1){
    		to[++tot]=v;nex[tot]=fir[u];fir[u]=tot;w[tot]=c;
    		if(flag) add(v,u,c,0);
    	}
    	inline void clear(){std::memset(fir,0,sizeof fir);tot=0;}
    	inline void read(int n){for(int i=1,u,v;i<n;i++) u=::read(),v=::read(),add(u,v,readl());}
    	int st[LOG_N+2][N*2],id[N*2];
    	int deep[N],dfscnt,dfn[N];
    	long long sum[N];
    	void dfs(int u,int fa=0){
    		deep[u]=deep[fa]+1;dfn[u]=++dfscnt;id[dfscnt]=u;
    		for(int v,i=fir[u];i;i=nex[i]){
    			v=to[i];
    			if(v==fa) continue;
    			sum[v]=sum[u]+w[i];
    			dfs(v,u);id[++dfscnt]=u;
    		}
    	}
    	inline void initLca(){
    		dfs(1);st[0][1]=id[1];
    		for(int i=2;i<=dfscnt;i++) lg2[i]=lg2[i>>1]+1,st[0][i]=id[i];
    		for(int j=1;j<=LOG_N;j++)for(int i=1;i+(1<<j)<=dfscnt;i++) st[j][i]=deep[st[j-1][i]]<deep[st[j-1][i+(1<<(j-1))]]?st[j-1][i]:st[j-1][i+(1<<(j-1))];
    	}
    	inline int getLca(int u,int v)const{
    		if(dfn[u]>dfn[v]) lib::swap(u,v);
    		u=dfn[u];v=dfn[v];
    		int o=lg2[v-u+1];
    		return deep[st[o][u]]<deep[st[o][v-(1<<o)+1]]?st[o][u]:st[o][v-(1<<o)+1];
    	}
    	inline long long getDis(const int &u,const int &v)const{return sum[u]+sum[v]-(sum[getLca(u,v)]<<1);}
    }G,T2,T3;//G: T1
    long long ans;
    long long dis[N];//for T1(G)
    int color[N];//for T1(G)
    namespace VirtualT{
    inline long long calc(const int &u,const int &v){return (u&&v)?(dis[u]+dis[v]+T2.sum[u]+T2.sum[v]+T3.getDis(u,v)):0;}
    struct Node{
    	int x,y;
    	long long dis;
    	inline void init(){x=y=dis=0;}
    	inline Node(){};
    	inline Node(const int &a,const int &b){x=a;y=b;dis=calc(a,b);}
    	inline void operator = (const Node &o){x=o.x;y=o.y;dis=o.dis;}
    	inline void chkMax(const Node &o){(o.dis>dis)&&(x=o.x,y=o.y,dis=o.dis);}
    	inline Node operator + (const Node &o)const{
    		if(!x) return o;
    		if(!o.x) return *this;
    		Node ans=*this;ans.chkMax(o);
    		ans.chkMax(Node(x,o.x));ans.chkMax(Node(x,o.y));
    		ans.chkMax(Node(y,o.x));ans.chkMax(Node(y,o.y));
    		return ans;
    	}
    };
    Node f[N][2];
    inline void update(int u,int v){
    	lib::chkMax(ans,calc(f[u][0].x,f[v][1].x)-(T2.sum[u]<<1));lib::chkMax(ans,calc(f[u][0].x,f[v][1].y)-(T2.sum[u]<<1));
    	lib::chkMax(ans,calc(f[u][0].y,f[v][1].x)-(T2.sum[u]<<1));lib::chkMax(ans,calc(f[u][0].y,f[v][1].y)-(T2.sum[u]<<1));
    	lib::chkMax(ans,calc(f[u][1].x,f[v][0].x)-(T2.sum[u]<<1));lib::chkMax(ans,calc(f[u][1].x,f[v][0].y)-(T2.sum[u]<<1));
    	lib::chkMax(ans,calc(f[u][1].y,f[v][0].x)-(T2.sum[u]<<1));lib::chkMax(ans,calc(f[u][1].y,f[v][0].y)-(T2.sum[u]<<1));
    	f[u][0]=f[u][0]+f[v][0];f[u][1]=f[u][1]+f[v][1];
    }
    inline void build(int n,int *node,const Graph &G){//build virtual tree from G
    	std::sort(node+1,node+1+n,[&](const int &a,const int &b){return G.dfn[a]<G.dfn[b];});
    	static int stack[N],top;
    	top=0;stack[++top]=1;
    	if(node[1]==1) f[1][color[1]]=Node(1,1),f[1][color[1]^1].init();
    	else f[1][1].init(),f[1][0].init();
    	for(int lca,i=1+(node[1]==1);i<=n;i++){
    		lca=G.getLca(node[i],stack[top]);
    		if(lca^stack[top]){
    			while(G.dfn[lca]<G.dfn[stack[top-1]]) update(stack[top-1],stack[top]),top--;
    			if(lca^stack[top-1]){
    				f[lca][0].init();f[lca][1].init();
    				update(lca,stack[top]);stack[top]=lca;
    			}
    			else update(lca,stack[top--]);
    		}
    		f[node[i]][color[node[i]]]=Node(node[i],node[i]);f[node[i]][color[node[i]]^1].init();stack[++top]=node[i];
    	}
    	while(--top) update(stack[top],stack[top+1]);
    }
    inline void work(int n,int *node){build(n,node,T2);}
    }//namespace VirtualT
    namespace Divide{
    int root,size[N],maxson[N],vis[N];
    int node[N];
    void dfs(int u,std::vector<int>&ve,int fa=0){
    	ve.push_back(u);
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa||vis[v]) continue;
    		dis[v]=dis[u]+G.w[i];dfs(v,ve,u);
    	}
    }
    struct Node{
    	int u,size;
    	inline int operator < (const Node &o)const{return size==o.size?u<o.u:size<o.size;}
    };
    std::vector<int>ve[N];
    inline void calc(int u,int tot){//tot: size of the block
    	std::set<Node>set;
    	static int node[N];
    	dis[u]=0;
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(vis[v]) continue;
    		dis[v]=dis[u]+G.w[i];
    		dfs(v,ve[v]);set.insert((Node){v,size[v]>size[u]?(tot-size[u]):size[v]});
    	}
    	while(set.size()>=2){
    		Node aa=*set.begin(),bb=*(++set.begin());
    		set.erase(set.begin());set.erase(set.begin());
    		int a=aa.u,b=bb.u;
    		node[0]=0;
    		if(ve[a].size()>ve[b].size()) std::swap(a,b);
    		for(int v:ve[b]) node[++node[0]]=v,color[v]=1;
    		for(int v:ve[a]) node[++node[0]]=v,ve[b].push_back(v),color[v]=0;
    		VirtualT::work(node[0],node);
    		set.insert((Node){b,aa.size+bb.size});
    	}
    	node[0]=0;node[++node[0]]=u;color[u]=0;
    	int a=(*set.begin()).u;
    	for(int v:ve[a]) node[++node[0]]=v,color[v]=1;
    	VirtualT::work(node[0],node);
    	for(int i=G.fir[u];i;i=G.nex[i]) std::vector<int>().swap(ve[G.to[i]]);
    }
    void findRoot(int u,int fa=0){
    	size[u]=1;maxson[u]=0;
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(v==fa||vis[v]) continue;
    		findRoot(v,u);size[u]+=size[v];
    		lib::chkMax(maxson[u],size[v]);
    	}
    	lib::chkMax(maxson[u],size[0]-size[u]);
    	if(maxson[u]<maxson[root]) root=u;
    }
    void divide(int u,int tot){
    	vis[u]=1;calc(u,tot);
    	for(int v,i=G.fir[u];i;i=G.nex[i]){
    		v=G.to[i];
    		if(vis[v]) continue;
    		root=0;size[0]=size[v]>size[u]?(tot-size[u]):size[v];
    		findRoot(v);divide(root,size[v]);
    	}
    }
    inline void work(int n){
    	root=0;maxson[0]=_INT_INF;size[0]=n;
    	findRoot(1);divide(root,size[1]);
    }
    }//namespace Divide
    int main(){
    		#ifdef LOCAL
    		freopen("tunnel2.in","r",stdin);
    		#endif
    	int n=read();
    	G.read(n);T2.read(n);T3.read(n);
    	T2.initLca();T3.initLca();
    	Divide::work(n);
    	writeEN(ans);
    	return RET;
    }
    
  • 相关阅读:
    转:VS2017常用快快捷键
    转:SQL Server中常用的快捷键
    转:SQL server中转换大小写快捷键
    转:left join 和 left outer join 的区别
    如何修改SVN的地址
    转 Echars地图详解
    HTML5 -- 使用css3实现简单的响应式布局
    Mac上用终端管理MySQL
    DDL 语句
    python 快速写作技巧,格式
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/15959185.html
Copyright © 2020-2023  润新知