[NOIP2015] 運輸計劃

Solution:Solution:

Subtask 1:20pts\text{Subtask 1}: 20pts

m=1m=1

好像很可做
O(n)O(n)暴力一下找路徑上最長的一條邊刪去即可

Subtask 2:30pts\text{Subtask 2}:30pts
暴力刪邊,暴力跑O(n2m)O(n^2m),應該能比Subtask 1\text{Subtask 1}多拿兩個點#2,#3
就是25分到手

Subtask 3:50pts\text{Subtask 3}:50pts
我們還是暴力刪邊,但是維護樹上路徑,我們想到了樹剖,所以用樹剖維護一下O(nmlog2n)O(nmlog^2n)
應該可以拿到#1-6,#8-9,#11-12一共10個點
加油,tgtgd2t3d2t3我們已經拿到一半了

Subtask 4:55pts\text{Subtask 4}:55pts
我們看之前沒過的#7 ,他給出的特殊條件是一條鏈
一條鏈就很好搞啦,弄個前綴和維護一下,還是暴力刪邊,但是每次換刪哪條邊的時候需要重新跑一邊前綴和,複雜福是O(n(n+m))O(n\cdot (n+m)),但是隻能過#7,不能過下面的那四個鏈的點

Subtask 5:75pts\text{Subtask 5}:75pts
我們發現,對於鏈的情況,其實我們就是要維護區間和, 還有單點修改,所以我們想到了線段樹!
我們用線段樹維護鏈上的區間和,這樣O(mlogn)O(mlogn)我們就可以把所有鏈的情況都過了

Subtask 6:100pts\text{Subtask 6}:100pts
上面說了一些Subtask\text{Subtask}的做法,現在來說一種正解
話說洛谷題解裏好像沒人寫這種做法
題目裏面說到

如果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多少?

所以題目讓我們求的是所有航線中最長的那條的最小值
所以我們想到了二分,但是二分其實並不好做(可以二分+lca+樹上差分,複雜度是O(mlognlogti)O(mlognlog\sum t_i))在nn特別大的時候其實複雜度並不是很優秀

我們再回頭看一眼題,是一個樹上的問題,沒有動態刪邊連邊的操作,排除了lctlct,那麼處理樹上問題最常見的方法就是樹鏈剖分了
所以我們把他樹剖一下

然後我們考慮最後的答案,我們刪的邊一定是最長的kk條路徑上的交集(最長的kk條路徑一定都經過我們刪的哪條邊)中的最長的一條,爲什麼呢?比如說第二長的我們沒有選,那麼答案一定不會比第二長的路徑的長度短(性質1)

然後我們又注意到一條性質,當第kk長的邊沒有覆蓋的時候,我們再要求刪掉的邊在第k+1k+1長的路徑上的時候是沒有意義的,因爲約束條件變多一定不會比此前的答案更優(性質2)

考慮答案是什麼,當我們找出了同時覆蓋前kk大路徑的邊中最長的那個作爲刪掉的邊,那麼答案有可能是
1.最長的路徑的長度-刪掉的邊的長度
2.第k+1k+1條路徑的最大值
而對於第22-kk長的路徑和答案一定是無關的
那麼這個時候答案就是1,2取個max\max

所以我們形成了一個初步的思路,就是首先樹剖一下,求出每個路徑的長度,爲了便於處理,我們按路徑長度降序排序ii從1到m循環,根據上面的算出答案。當沒有一條邊滿足同時位於前kk大路徑的時候,根據性質1,2,直接break掉就可以了

那麼我們現在需要做的是
1.求路徑長度
2.判斷是否有一條路徑能夠滿足同時被前ii長的路徑所覆蓋
3.求滿足條件的中最長的

對於1. ,樹剖板子
對於2. ,我們可以另開一個計數器timtim表示這個點被覆蓋了多少次,我們每次新加入一條路徑的時候,我們把這條路徑上的timtim都+1,判斷一下這條路徑上的最大值,如果等於ii,說明有,如果小於ii,那麼就是沒有
對於3. ,我們可以再開一個變量儲存所有被覆蓋的次數等於tim的邊的邊權最大值,那麼稍微改一下pushup就可以了

void pushup(int u){
	seg[u].val=seg[lc].val+seg[rc].val;
	if(seg[lc].tim>seg[rc].tim)seg[u].tim=seg[lc].tim,seg[u]._max=seg[lc]._max;
	if(seg[rc].tim>seg[lc].tim)seg[u].tim=seg[rc].tim,seg[u]._max=seg[rc]._max;
	if(seg[lc].tim==seg[rc].tim)seg[u].tim=seg[lc].tim,seg[u]._max=max(seg[lc]._max,seg[rc]._max);
}

但是我們查詢2,3應該同時進行,所以我們需要返回一個結構體類型,我是爲了節省空間用的pair,要不然我就像往常 一樣 直接 用線段樹的結構體了qaq

這個做法的複雜度應該是O(n+n+n+n+mlog2n+mlogm+mlog2n)=O(mlog2n)O(n+n+n+n+mlog^2n+mlogm+mlog^2n)=O(mlog^2n)
(輸入邊+dfs1+dfs2+build+查詢邊長+排序+查詢)

n,m都是3×1053\times10^5的時候是不用像之前說的二分那樣去卡常的
而且這種做法除了代碼長點也沒有什麼細節,唯一的細節就是答案怎麼算啦,而且長也主要是正常的板子啦

上代碼(總1.89s):

# include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
# define debug puts("QAQ");

typedef long long ll;
const int N=3e5+5;
const int mod=1e9+7;
const double eps=1e-7;

template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,m,ans=INT_MAX;
int head[N],cnt;
int faz[N],son[N],dep[N],siz[N],dfn[N],top[N],tot;
int a[N],_a[N];

struct Edge{
	int to,next,w;	
}e[N<<1];

struct querys{
	int x,y,t;
	bool operator < (const querys &cmp)const{
		return t>cmp.t;
	}
}q[N];

void add(int x,int y,int c){
	e[++cnt]=(Edge){y,head[x],c},head[x]=cnt;	
}

struct segment_tree{
	int l,r;
	int val,tim,_max;//val表示這一區間的長度,tim表示這條邊被標記過幾次,_max表示這個區間內被標記過tim次的邊的最長權值 
	int tag;	
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	seg[u].val=seg[lc].val+seg[rc].val;
	if(seg[lc].tim>seg[rc].tim)seg[u].tim=seg[lc].tim,seg[u]._max=seg[lc]._max;
	if(seg[rc].tim>seg[lc].tim)seg[u].tim=seg[rc].tim,seg[u]._max=seg[rc]._max;
	if(seg[lc].tim==seg[rc].tim)seg[u].tim=seg[lc].tim,seg[u]._max=max(seg[lc]._max,seg[rc]._max);
}

void pushdown(int u){
	seg[lc].tim+=seg[u].tag;
	seg[rc].tim+=seg[u].tag;
	seg[lc].tag+=seg[u].tag;
	seg[rc].tag+=seg[u].tag;
	seg[u].tag=0;	
}

pair<int,int> merge(pair<int,int> l,pair<int,int> r){
	if(l.first>r.first)return l;
	if(r.first>l.first)return r;
	if(l.first==r.first)return make_pair(l.first,max(l.second,r.second));
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){
		seg[u].val=seg[u]._max=_a[l];
		seg[u].tim=0;
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);
}

void update(int u,int l,int r,int k){
	if(seg[u].l>=l&&seg[u].r<=r){
		seg[u].tim++;
		seg[u].tag++;
		return;
	}
	if(seg[u].tag)pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	if(l<=mid)update(lc,l,r,k);
	if(r>mid)update(rc,l,r,k);
	pushup(u);
}

int Getlen(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u].val;
	if(seg[u].tag)pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	int res=0;
	if(l<=mid)res+=Getlen(lc,l,r);
	if(r>mid)res+=Getlen(rc,l,r);
	return res;	
}

pair<int,int> query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return make_pair(seg[u].tim,seg[u]._max);
	if(seg[u].tag)pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	return merge(query(lc,l,r),query(rc,l,r));	
}

void RouteModify(int x,int y,int k){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		update(1,dfn[top[x]],dfn[x],k);
		x=faz[top[x]];
	}
	if(x!=y){
		if(dep[x]>dep[y])swap(x,y);
		update(1,dfn[x]+1,dfn[y],k);	
	}
}

int RouteQuerylen(int x,int y){
	int res=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res+=Getlen(1,dfn[top[x]],dfn[x]);
		x=faz[top[x]];	
	}
	if(x!=y){
		if(dep[x]>dep[y])swap(x,y);
		res+=Getlen(1,dfn[x]+1,dfn[y]);	
	}
	return res;
}

pair<int,int> RouteQuery(int x,int y){
	pair<int,int> res;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=merge(res,query(1,dfn[top[x]],dfn[x]));
		x=faz[top[x]];
	}
	if(x!=y){
		if(dep[x]>dep[y])swap(x,y);
		res=merge(res,query(1,dfn[x]+1,dfn[y]));	
	}
	return res;
}

void dfs1(int u,int fa){
	faz[u]=fa;
	siz[u]=1;
	dep[u]=dep[fa]+1;
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		a[v]=e[i].w;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;	
	}
}

void dfs2(int u,int _top){
	top[u]=_top;
	dfn[u]=++tot;
	_a[tot]=a[u];
	if(!son[u])return;
	dfs2(son[u],_top);
	RepG(i,u){
		int v=e[i].to;
		if(v==faz[u]||v==son[u])continue;
		dfs2(v,v);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	read(n),read(m);
	Rep(i,1,n-1){
		int x,y,c;
		read(x),read(y),read(c);
		add(x,y,c),add(y,x,c);
	}
	dfs1(1,0),dfs2(1,1);
	build(1,1,n);
	Rep(i,1,m){
		read(q[i].x),read(q[i].y);
		q[i].t=RouteQuerylen(q[i].x,q[i].y);
	} 
	sort(q+1,q+m+1);
	ans=q[1].t;
	Rep(i,1,m){
		RouteModify(q[i].x,q[i].y,1);
		pair<int,int> res=RouteQuery(q[i].x,q[i].y);
		if(res.first<i)
			break;
		ans=min(ans,max(q[1].t-res.second,q[i+1].t));
	}
	printf("%d\n",ans);
	return 0;
}

第一道自己寫的day2T3day2T3耶,開森

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章