zoukankan      html  css  js  c++  java
  • 洛谷 P3783

    题面传送门

    神仙题一道。

    首先注意到这里的贡献涉及到边的顺序,并且只与相邻的边是什么有关,因此不难想到一个做法——边转点,点转边,具体来说对于每条边 (e),我们将其拆成两个点 (in_e,out_e),并连边 (in_e o out_e),权值为 (c_e),同时对于所有 (b_i=a_j) 的边 (i,j),连边 (out_i o in_j),权值为 (dep[ ext{LCA}(d_i,d_j)]),以及对于所有 (a_i=1) 的边连 (S o in_i),权值为 (0) 的边,跑最短路,最后 (ans_i=minlimits_{b_e=i}dis_{out_e})。正确性显然。

    然而该做法效率低下,边数最高可达到 (mathcal O(m^2)),一脸过不去的亚子。于是又到了优化建图的时间了,一个 observation 是对于一个点 (u) 而言,我们在 (b_i=a_j=u) 的边 (i,j) 之间连边,这些边边权的种类是不会太多的,因为假设 (S={d_i|b_i=ulor a_i=u}),那么这些边权肯定只能(S) 当中某两个点的 ( ext{LCA}) 的深度,根据虚树那一套理论,(S) 当中任意两点的 ( ext{LCA}) 总共只有 (mathcal O(|S|)) 种可能。而对于所有点 (u),它们对应 (|S|) 的总和为 (mathcal O(m)) 级别,这个是在我们能接受的范围内的。因此考虑从这个性质入手优化这道题,我们将 (S) 当中的点按 DFS 序排个序,那么根据虚树中一个定理:对于待建立虚树的 (m) 个点 (a_1,a_2,cdots,a_m),假设将其按照 DFS 序排序得到 (b_1,b_2,cdots,b_m),那么记 (h_i=dep[ ext{LCA}(a_i,a_{i+1})]),有 (dep[ ext{LCA}(b_l,b_r)]=minlimits_{i=l}^{r-1}h_i)(和 SA 有点像),因此考虑按照套路求出 (h_i),那么问题可以转化为:

    • 有一排 (t) 个点,每个点有两种类型 (0/1),每两个点中间写有一个数 (h_i),我们要从所有 (0) 点向 (1) 点连边,权值为它们之间数的最小值。

    这个有一个显然的做法——对于某个 (h_x),满足 (minlimits_{i=l}^{r-1}h_i=h_x)(l,r) 肯定分别在一段区间内,单调栈求出对应的区间,线段树优化建图即可,但是这个做法多一个 (log),不知道能不能通过,而且实现起来过于麻烦,因此我们还需考虑更简便做法。考虑最短路的一个性质,就是对于两点间如果有多条边的情况,那么显然只有权值最小的边是有用的,其他边可连可不连——连了也不影响你最短路的大小。因此考虑对于某个 (h_i),我们不维护什么单调栈,直接从 ([1,i]) 中的 (0) 点向 ([i+1,t]) 中的 (1) 点连边,([i+1,t]) 中的 (0) 权点向 ([1,i]) 中的 (1) 点连边,根据上面的结论,对于某个 (l,r),只有 ([l,r))(h) 的最小值(设为 (h_x))是有用的,其他边连上也没事,而显然在考虑 (x) 的贡献时,已经连了 (l,r) 之间的边,因此最短路跑出来依然是对的。考虑优化,注意到每次都是某个前缀向后缀,或者后缀向前缀连边,因此考虑前后缀优化建图,具体来说建立四排点——前缀 in,前缀 out,后缀 in,后缀 out,然后随便瞎连连即可,具体见代码。

    时间复杂度 (mathcal O(Tmlog m)),然鹅常数非常大,直接上天……

    const int MAXN=5e4;
    const int MAXK=2e4;
    const int LOG_N=16;
    const int MAXV=6e5+1;
    const int MAXE=1e7;
    int n,m,k,S=6e5+1,ncnt;
    int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],val[MAXE+5],ec=0;
    void adde(int u,int v,int w){
    //	printf("%d %d %d
    ",u,v,w);
    	to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;
    }
    struct edge{int a,b,c,d;} e[MAXN+5];
    vector<int> in[MAXN+5],out[MAXN+5],g[MAXK+5];
    int dfn[MAXK+5],fa[MAXK+5][LOG_N+2],dep[MAXK+5],tim=0;
    void dfs(int x=1,int f=0){
    	dfn[x]=++tim;fa[x][0]=f;
    	for(int y:g[x]) dep[y]=dep[x]+1,dfs(y,x);
    }
    int getlca(int x,int y){
    	if(dep[x]<dep[y]) x^=y^=x^=y;
    	for(int i=LOG_N;~i;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
    	if(x==y) return x;
    	for(int i=LOG_N;~i;i--) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    bool cmp(pii x,pii y){return dfn[x.fi]<dfn[y.fi];}
    ll dis[MAXV+5],ans[MAXN+5];
    void clear(){
    	S=6e5+1;ncnt=0;memset(hd,0,sizeof(hd));ec=0;
    	memset(dfn,0,sizeof(dfn));memset(fa,0,sizeof(fa));
    	memset(dep,0,sizeof(dep));tim=0;
    	memset(dis,63,sizeof(dis));memset(ans,63,sizeof(ans));
    	for(int i=1;i<=MAXN;i++) in[i].clear(),out[i].clear();
    	for(int i=1;i<=MAXK;i++) g[i].clear();
    }
    void solve(){
    	scanf("%d%d%d",&n,&m,&k);clear();ncnt=m<<1;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].c,&e[i].d);
    		out[e[i].a].pb(i);in[e[i].b].pb(i);
    	}
    	for(int i=1,u,v;i<k;i++) scanf("%d%d%*d",&u,&v),g[u].pb(v);dfs(1,0);
    	for(int i=1;i<=LOG_N;i++) for(int j=1;j<=k;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
    	for(int i=1;i<=m;i++) adde(i,i+m,e[i].c);
    	for(int i=1;i<=m;i++) if(e[i].a==1) adde(S,i,0);
    	for(int i=1;i<=n;i++){
    		vector<pii> ed;
    		for(int x:in[i]) ed.pb(mp(e[x].d,x+m));
    		for(int x:out[i]) ed.pb(mp(e[x].d,x));
    		sort(ed.begin(),ed.end(),cmp);
    		vector<int> h(ed.size(),0);
    		vector<int> pre_in(ed.size(),0),pre_out(ed.size(),0);
    		vector<int> suf_in(ed.size(),0),suf_out(ed.size(),0);
    		for(int j=0;j+1<ed.size();j++) h[j]=dep[getlca(ed[j].fi,ed[j+1].fi)];
    //		printf("node %d
    ",i);
    //		for(int j=0;j<ed.size();j++) printf("pts %d %d
    ",ed[j].fi,ed[j].se);
    //		for(int j=0;j+1<ed.size();j++) printf("%d
    ",h[j]);
    		for(int j=0;j<ed.size();j++) pre_in[j]=++ncnt;
    		for(int j=0;j<ed.size();j++) pre_out[j]=++ncnt;
    		for(int j=0;j<ed.size();j++) suf_in[j]=++ncnt;
    		for(int j=0;j<ed.size();j++) suf_out[j]=++ncnt;
    		for(int j=0;j+1<ed.size();j++) adde(pre_in[j+1],pre_in[j],0);
    		for(int j=0;j+1<ed.size();j++) adde(pre_out[j],pre_out[j+1],0);
    		for(int j=0;j+1<ed.size();j++) adde(suf_in[j],suf_in[j+1],0);
    		for(int j=0;j+1<ed.size();j++) adde(suf_out[j+1],suf_out[j],0);
    		for(int j=0;j<ed.size();j++){
    			if(ed[j].se<=m) adde(suf_in[j],ed[j].se,0),adde(pre_in[j],ed[j].se,0);
    			else adde(ed[j].se,suf_out[j],0),adde(ed[j].se,pre_out[j],0);
    		}
    		for(int j=0;j+1<ed.size();j++){
    			adde(suf_out[j+1],pre_in[j],h[j]);
    			adde(pre_out[j],suf_in[j+1],h[j]);
    		}
    	} memset(dis,63,sizeof(dis));memset(ans,63,sizeof(ans));
    	priority_queue<pii,vector<pii>,greater<pii> > q;
    	q.push(mp(dis[S]=0,S));
    	while(!q.empty()){
    		pii p=q.top();q.pop();
    		int sum=p.fi,x=p.se;
    		if(dis[x]<sum) continue;
    		for(int e=hd[x];e;e=nxt[e]){
    			int y=to[e],z=val[e];
    			if(dis[y]>dis[x]+z) q.push(mp(dis[y]=dis[x]+z,y));
    		}
    	}
    	for(int i=1;i<=m;i++) chkmin(ans[e[i].b],dis[i+m]);
    	for(int i=2;i<=n;i++) printf("%lld
    ",ans[i]);
    }
    int main(){int qu;scanf("%d",&qu);while(qu--) solve();return 0;}
    
  • 相关阅读:
    [转]ASP.NET 2.0 数据绑定表达式语法
    为什么不直接使用成员变量而使用属性呢?
    WebForm中的容器
    winform下简单多线程例子
    Window.ShowModalDialog使用手册
    weblog? weblogPost?
    浅析Page.LoadTemplate("模板")方法动态获取绑定模板后,通过FindControl获取服务端控件的方法。
    在sql中快速导入、导出Excel
    游标
    sql判断文件是否存在
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P3783.html
Copyright © 2011-2022 走看看