• [bzoj4860] [loj#2179] [BJOI2017] 树的难题


    题意简述

    (n) 个点的无根树。
    树上每条边有颜色。共 (m) 种颜色,第 (i) 种颜色有权值 (c_i)
    对于树上一条简单路径,路径上经过的所有边按顺序组成一个颜色序列,序列可以划分成若干个相同颜色段。定义路径权值为颜色序列上每个同颜色段的颜色权值之和。
    求长度在 ([l,r]) 的路径权值最大值。

    想法

    容易想到点分治。然后大体框架就有了。
    问题是如何统计长度在 ([l,r]) 的路径的权值。
    容易想到在访问子节点时,同一个颜色的挨着访问,将访问完的路径分为“不同颜色”与“相同颜色”分别按长度排序,然后单调队列。
    然后TLE了几个点……

    问题在于,如果我们访问的第一个颜色的最长路径很长的话,访问后面点时每个点都进行一次单调队列,相当于这个“长路径”访问了很多很多遍。
    于是改变一下访问顺序。
    仍是同一个颜色的挨着访问,维护两个单调队列。
    将颜色按照此颜色的最大深度从小到大排序,将同一颜色中的点按该点可达的最大深度从小到大排序。
    每个点访问完后,与该颜色的单调队列跑一次;一个颜色中的点都访问后,将此颜色的单调队列合并至总的单调队列中。
    由于有排序,总复杂度 (O(nlog^2n))

    还有一种线段树的【暴力】做法:
    仍是同一个颜色的挨着访问。维护两个线段树,一个表示相同颜色,一个表示不同颜色。
    访问完一个点后,将访问到的各长度的路径在线段树中找区间最大值更新答案。
    访问完一个颜色后,两个线段树合并。合并复杂度 (O(nlogn))
    总复杂度 (O(nlog^2n)),但是常数较大。

    总结

    我就是觉得复杂度这个东西太玄妙了!!爱了爱了!
    顺序很重要!!

    代码

    写+调的我要裂开了。。。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    #define INF 1000000000
    
    using namespace std;
    
    int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch) && ch!='-') ch=getchar();
    	if(ch=='-') f=-1,ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x*f;
    }
    
    const int N = 200005;
    typedef long long ll;
    typedef pair<int,int> Pr;
    
    int n,m,L,R,cv[N];
    
    struct edge{
    	int u,v,c;
    	bool operator < (const edge &b) const{ return c<b.c; }
    }ed[N];
    struct node{
    	int v,c;
    	node *nxt;
    }pool[N*2],*h[N];
    int cnt;
    void addedge(int u,int v,int c){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->nxt=h[u];h[u]=p;p->c=c;
    	q->v=u;q->nxt=h[v];h[v]=q;q->c=c;
    }
    
    int root,all,sz[N],mx[N],vis[N];
    void getrt(int u,int fa){
    	int v;
    	sz[u]=1; mx[u]=0;
    	for(node *p=h[u];p;p=p->nxt)
    		if((v=p->v)!=fa && !vis[v]){
    			getrt(v,u);
    			sz[u]+=sz[v];
    			mx[u]=max(mx[u],sz[v]);
    		}
    	mx[u]=max(mx[u],all-sz[u]);
    	if(mx[u]<mx[root]) root=u;
    }
    int dis[N];
    void dfs_sz(int u,int fa){
    	int v;
    	sz[u]=1;
    	dis[u]=0;
    	for(node *p=h[u];p;p=p->nxt)
    		if((v=p->v)!=fa && !vis[v]){
    			dfs_sz(v,u);
    			dis[u]=max(dis[u],dis[v]);
    			sz[u]+=sz[v];
    		}
    	dis[u]++;
    }
    ll b[N],pre[N],cur[N];
    void cal(int u,int fa,int c,ll sum,int len){
    	cur[len]=max(sum+cv[c],cur[len]);
    	if(len>=R) return;
    	int v;
    	for(node *p=h[u];p;p=p->nxt)
    		if((v=p->v)!=fa && !vis[v]){
    			if(p->c==c) cal(v,u,c,sum,len+1);
    			else cal(v,u,p->c,sum+cv[c],len+1);
    		}
    }
    ll ans;
    int que[N],hd,tl,son[N],col[N],mxd[N];
    bool cmp(int x,int y){
    	if(col[x]==col[y]) return dis[x]<dis[y]; /**/
    	if(mxd[col[x]]==mxd[col[y]]) return col[x]<col[y];
    	return mxd[col[x]]<mxd[col[y]];
    }
    
    ll mxb[N];
    void work(int u){
    	int v,lastc=0,dep,tb=0,tp=0;
    	vis[u]=1;
    	
    	int sn=0;
    	for(node *p=h[u];p;p=p->nxt) 
    		if(!vis[v=p->v]){
    			son[sn++]=v;
    			col[v]=p->c;
    			dfs_sz(v,u);
    			dis[v]=min(dis[v],R);
    			mxd[col[v]]=max(mxd[col[v]],dis[v]);
    		} 
    	sort(son,son+sn,cmp);
    	for(int i=0;i<sn;i++) mxd[col[son[i]]]=0;
    	
    	for(int k=0;k<sn;k++){
    		v=son[k];
    		cal(v,u,col[v],0,1);
    		for(int i=L;i<=dis[v];i++) if(i<=R) ans=max(ans,cur[i]);
    		if(col[v]!=lastc){
    			hd=tl=0;
    			for(int i=tp,j=1;i>0;i--){
    				while(j<=tb && j+i<=R){
    					while(hd<tl && b[que[tl-1]]<=b[j]) tl--;
    					que[tl++]=j++;
    				}
    				while(hd<tl && que[hd]+i<L) hd++;
    				if(hd<tl) ans=max(ans,b[que[hd]]+pre[i]);
    			}
    			tb=tp;
    			for(int i=1;i<=tp;i++) b[i]=max(b[i],pre[i]),pre[i]=-INF;/**/
    			tp=dis[v];
    			for(int i=1;i<=dis[v];i++) pre[i]=cur[i];
    		}
    		else{
    			hd=tl=0;
    			for(int i=dis[v],j=1;i>0;i--){
    				while(j<=tp && j+i<=R){
    					while(hd<tl && pre[que[tl-1]]<=pre[j]) tl--;
    					que[tl++]=j++;
    				}
    				while(hd<tl && que[hd]+i<L) hd++;
    				if(hd<tl) ans=max(ans,pre[que[hd]]+cur[i]-cv[col[v]]);
    			} 
    			for(int i=1;i<=dis[v];i++) pre[i]=max(pre[i],cur[i]);
    			tp=dis[v];
    		}
    		lastc=col[v];
    		for(int i=1;i<=dis[v];i++) cur[i]=-INF;
    	}
    	hd=tl=0;
    	for(int i=tp,j=1;i>0;i--){
    		while(j<=tb && j+i<=R){
    			while(hd<tl && b[que[tl-1]]<=b[j]) tl--;
    			que[tl++]=j++;
    		}
    		while(hd<tl && que[hd]+i<L) hd++;
    		if(hd<tl) ans=max(ans,b[que[hd]]+pre[i]);
    	}
    	//clear
    	for(int i=0;i<=tb;i++) b[i]=-INF;
    	for(int i=0;i<=tp;i++) pre[i]=-INF;
    	
    	for(node *p=h[u];p;p=p->nxt)
    		if(!vis[v=p->v]){
    			root=0; all=sz[v]; getrt(v,u);
    			work(root);
    		}
    }
    
    int main()
    {
    	n=read(); m=read(); L=read(); R=read();
    	for(int i=1;i<=m;i++) cv[i]=read();
    	for(int i=1;i<n;i++){ ed[i].u=read(); ed[i].v=read(); ed[i].c=read(); }
    	sort(ed+1,ed+n);
    	for(int i=1;i<n;i++) addedge(ed[i].u,ed[i].v,ed[i].c);
    	
    	sz[0]=mx[0]=n+1;
    	root=0; all=n; getrt(1,0);
    	
    	for(int i=0;i<=n;i++) b[i]=-INF,pre[i]=-INF,cur[i]=-INF;
    	ans=-INF;work(root);
    	
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    支付系统整体架构
    犹太”安息日”
    JWT(JSON Web Token) 【转载】
    详解布隆过滤器的原理、使用场景和注意事项
    缓存一致性策略以及雪崩、穿透问题 【零壹技术栈】
    RPC概念及分类【转载】
    RDLC 微软报表 导出Excel时产生多个工作表 (worksheet)
    asp.net 5 (mvc 6) 获取网站的物理路径
    Asp.net 5 (MVC6) Areas 分区
    MVC6 OWin Microsoft Identity 自定义验证
  • 原文地址:https://www.cnblogs.com/lindalee/p/12295013.html
Copyright © 2020-2023  润新知