1. 程式人生 > >2018.11.05【NOIP2015】【洛谷2680】【UOJ#150】運輸計劃(二分答案+DFS序+樹上差分)或(複雜度並不對(也不能過)的樹鏈剖分)

2018.11.05【NOIP2015】【洛谷2680】【UOJ#150】運輸計劃(二分答案+DFS序+樹上差分)或(複雜度並不對(也不能過)的樹鏈剖分)

洛谷傳送門

解析:

UOJ上的資料很強,複雜度不對過不了的,但是LCALCA如果是用倍增求的話也過不了(已經加了上界優化)。。。畢竟樹剖常數小,複雜度還不滿。。。

思路:

首先,不要試圖化邊為點,每條邊的資訊可以存在它所指向的兒子中。

解法1:UOJ上不能過的樹鏈剖分

其實我們只需要考慮斷掉哪些邊就行了,顯然斷掉最長路徑上以外的邊是沒有作用的,所以我們只需要考慮列舉斷掉最長路徑上的邊就行了。

而經過這條邊的其他路徑其實也會減小,不可能成為新的最長路徑,所以我們還需要記錄沒有經過這條邊的最長路徑,然後用 原最長路徑減去斷掉的邊 和沒有經過這條邊的最長路徑中較大的來更新答案。

維護最大值可以使用線段樹+DFS序來做,每次處理出沒有被該路徑覆蓋的部分,然後線段樹區間更新最大值就行了。

最後直接在最長鏈上面跑一邊就好了。 複雜度瓶頸在於更新最大值O(nlog2n)O(n\log^2n)

解法2:複雜度十分優秀的二分答案+樹上差分

首先,這個顯然的二分應該不需要解釋,解的存在性滿足單調性質。

那麼怎麼checkcheck

考慮所有長度大於當前二分答案的路徑都需要斷邊,那麼我們直接樹上差分按照DFSDFS序倒著跑一遍就可以知道哪些邊是被所有不滿足當前答案的覆蓋的,然後依次斷開,看最大值是否小於二分的答案就行了。

這樣做顯然是正確的,因為我們所有的二分答案只考慮當前不滿足答案的需要變小的路徑,而這些路徑全部變小後顯然只需要考慮它們當中最後的最大值是否能夠滿足要求,不然就肯定g

ggg

程式碼(樹鏈剖分):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<
3)+(c^48); return num; } cs int N=300005; int last[N],nxt[N<<1],to[N<<1],ecnt; int w[N<<1]; inline void addedge(int u,int v,int val){ nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val; nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,w[ecnt]=val; } int n,m; int fa[N],siz[N],top[N],son[N],dep[N],dist[N],pre[N]; inline void dfs1(int u){ siz[u]=1; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; dep[v]=dep[u]+1; dist[v]=dist[u]+(pre[v]=w[e]); fa[v]=u; dfs1(v); siz[u]+=siz[v]; if(siz[v]>siz[son[u]])son[u]=v; } } int in[N],pos[N],tot; inline void dfs2(int u){ pos[in[u]=++tot]=u; if(son[u]){ top[son[u]]=top[u]; dfs2(son[u]); } for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==son[u]||v==fa[u])continue; top[v]=v; dfs2(v); } } inline void tree_dissection(int root=1){ dfs1(root); top[root]=root; dfs2(root); } inline int LCA(int u,int v){ while(top[u]!=top[v]){ if(dep[top[u]]>dep[top[v]])swap(u,v); v=fa[top[v]]; } return dep[u]>dep[v]?v:u; } int maxn[N<<2],tag[N<<2]; inline void pushup(int k){maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);} inline void pushnow(int k,int val){ maxn[k]=max(maxn[k],val); tag[k]=max(tag[k],val); } inline void pushdown(int k){ if(tag[k]){ pushnow(k<<1,tag[k]); pushnow(k<<1|1,tag[k]); tag[k]=0; } } inline void update(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){ if(ql<=l&&r<=qr)return pushnow(k,val); int mid=(l+r)>>1;pushdown(k); if(ql<=mid)update(k<<1,l,mid,ql,qr,val); if(qr>mid)update(k<<1|1,mid+1,r,ql,qr,val); pushup(k); } inline int query(int k,int l,int r,cs int &pos){ if(l==r)return maxn[k]; int mid=(l+r)>>1;pushdown(k); if(pos<=mid)return query(k<<1,l,mid,pos); else return query(k<<1|1,mid+1,r,pos); } inline void update(int u,int v,int val){ vector<pair<int,int> > a; while(top[u]!=top[v]){ if(dep[top[u]]>dep[top[v]])swap(u,v); a.push_back(make_pair(in[top[v]],in[v])); v=fa[top[v]]; } if(dep[u]>dep[v])swap(u,v); a.push_back(make_pair(in[u]+1,in[v])); sort(a.begin(),a.end()); if(a[0].first>1)update(1,1,n,1,a[0].first-1,val); if(a.back().second<n)update(1,1,n,a.back().second+1,n,val); for(int re i=1;i<a.size();++i)update(1,1,n,a[i-1].second+1,a[i].first,val); } int mxdis,tu,tv; inline int getans(int u,int v){ int ans=mxdis; if(u==v)return 0; if(dep[u]>dep[v])swap(u,v); while(u^v){ if(dep[u]>dep[v]){ ans=min(ans,max(mxdis-pre[u],query(1,1,n,in[u]))); u=fa[u]; } else { ans=min(ans,max(mxdis-pre[v],query(1,1,n,in[v]))); v=fa[v]; } } return ans; } signed main(){ n=getint(); m=getint(); for(int re i=1;i<n;++i){ int u=getint(),v=getint(),val=getint(); addedge(u,v,val); } tree_dissection(); for(int re i=1;i<=m;++i){ int u=getint(),v=getint(); int lca=LCA(u,v); int dis=dist[u]+dist[v]-2*dist[lca]; if(dis>=mxdis){ mxdis=dis; tu=u,tv=v; } update(u,v,dis); } cout<<getans(tu,tv); return 0; }

程式碼(二分答案+樹上差分):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=300005;
int last[N],nxt[N<<1],to[N<<1],ecnt;
int W[N<<1];
inline void addedge(int u,int v,int val){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,W[ecnt]=val;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,W[ecnt]=val;
}

int fa[N],dep[N],top[N],siz[N],son[N],dist[N],pre[N],in[N],pos[N],tot;
inline void dfs1(int u){
	siz[u]=1;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u])continue;
		dep[v]=dep[u]+1;
		fa[v]=u;
		dist[v]=dist[u]+(pre[v]=W[e]);
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

inline void dfs2(int u){
	pos[in[u]=++tot]=u;
	if(son[u]){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u]||v==son[u])continue;
		top[v]=v;
		dfs2(v);
	}
}

inline void tree_dissection(int root=1){
	dfs1(root);
	top[root]=root;
	dfs2(root)