【JZOJ 省选模拟】铺路 (road )

题目

Description

【JZOJ 省选模拟】铺路 (road )_第1张图片
Input
在这里插入图片描述

Output
在这里插入图片描述

Sample Input
样例输入 1:
4 4
1 3 4
2 4 8
1 2 2
3 4 3
样例输入 2:
3 2
1 2 3
2 3 4

Sample Output
样例输出 1:
-1
8
8
3
样例输出 2:
-1
-1

Data Constraint

【JZOJ 省选模拟】铺路 (road )_第2张图片

思路

首先,答案是单调不增的

并且容易证出,所有联通块大小为偶数

对于只回答最后一组询问,把边权排序,直到没有大小是技术的联通块就行

现在要推广到所有询问

考虑将时间看为x,边权看为y,这么看一条边就是平面上一个点。
将询问时间看为x,答案看为y,询问也变成了一个点。

我们就可以用分治乱搞。

代码

#include
using namespace std;
const int N=3e5+77;
int n,m;
int sz[N],fa[N],cnt,rk[N];
int ans[N];
struct E
{
	int x,y,w,id;
}e[N];
bool cmp(E a,E b){return a.w<b.w;}
int fi;
vector<E> b[N*4];
multiset<int> em;
int gf(int x)
{
	return fa[x]==0?x:gf(fa[x]);
}
struct yjy
{
	int x,y,w,irs;
};
stack<yjy> S;
void ins(int x,int l,int r,int tl,int tr,const E &a)
{
	if(l>tr||r<tl) return;
	if(tl<=l&&r<=tr)
	{
		b[x].push_back(a); return;
	}
	ins(x<<1,l,l+r>>1,tl,tr,a);
	ins(x<<1|1,(l+r>>1)+1,r,tl,tr,a);
}
void merge(E e)
{
	int x=gf(e.x),y=gf(e.y);
	if(rk[x]>rk[y])swap(x,y);
	if(x!=y)
	{
		em.insert(e.w);
		yjy a=(yjy){x,y,e.w,rk[x]==rk[y]};
		S.push(a);
		fa[x]=y;
		if(rk[y]==rk[x])rk[y]++;
		if((sz[y]&1)&&(sz[x]&1))cnt-=2;
		sz[y]+=sz[x];
	}
	else S.push((yjy){0,0,0,0});
}
void pop()
{
	yjy a=S.top();S.pop();
	if(a.x==0)return;
	sz[a.y]-=sz[a.x];
	fa[a.x]=0;
	if(a.irs) rk[a.y]--;
	em.erase(em.find(a.w));
	if((sz[a.y]&1)&&(sz[a.x]&1)) cnt+=2;
}
void work(int x,int l,int r)
{
	for(E t:b[x]) merge(t);
	if(l==r)
	{
		int LF=fi;
		if(cnt!=0)
		{
			int i=fi+1;
			for(;i<=m;i++)
			{
				if(cnt==0)break;
				if(e[i].id<=l)
				{
					merge(e[i]);
					ins(1,1,m,e[i].id,l-1,e[i]);
				}
			}
			fi=i-1;
		}
		if(cnt==0)ans[l]=*em.rbegin();
		for(int i=fi; i>LF; i--)if(e[i].id<=l)pop();
	}
	else
	{
		work(x<<1|1,(l+r>>1)+1,r);
		work(x<<1,l,l+r>>1);
	}
	for(int i=0; i<b[x].size(); i++)
		pop();
}
int main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	cin>>n>>m;
	for(int i=1,x,y,w; i<=m; i++)
	{
		scanf("%d%d%d",&x,&y,&w);
		e[i]=(E){x,y,w,i};
	}
	sort(e+1,e+1+m,cmp);
	for(int i=1; i<=n; i++) sz[i]=1;cnt=n;
	work(1,1,m);
	for(int i=1; i<=m; i++) if(ans[i]==0) printf("%d\n",-1);
		else printf("%d\n",ans[i]);
}

你可能感兴趣的:(题解,分治)