传送 差分约束

题意:

题目描述
\(X\) 国有 \(N\) 座城市,由 \(N-1\) 条道路连接形成了一棵树,每条边都有边权 \(wi\) 表示经过这条边需要 \(wi\) 的时间。为了方便出行,\(X\) 国计划在每座城市建造一座传送装置,它们两两之间可以进行传送。传送并不是即时的,初始时你需要给每个传送装置设置一个参数 \(ai\),从装置 \(i\) 传送到装置 \(j\) 需要花费 \(|ai-aj|\) 的时间。
\(X\) 国并不希望得不偿失的情况发生,因此不能有任意两个城市之间通过传送需要的时间超过走路时间。同时,由于传送装置受每个城市的地质情况限制,每个城市的传送装置参数 \(ai\) 只能是区间 \([li, ri]\) 之内。当然你也可以对城市的地质进行改造,花费 \(x\)的代价可以使所有城市能接受的参数区间扩大为 \([li- x, ri + x]\)。代价必须是一个非负整数。
问在不进行改造的情况下,能否找到一种安排 \(ai\) 的方案,满足上述所有要求。有时你还需要回答在允许进行改造的情况下,最少花费多少代价进行改造,可以找到一种方案满足要求 (如果不需要改造就可满足则输出 \(0\))。
输入格式
第一行两个整数 \(T,typ\),表示数据组数和数据类型。
对于每组数据,第一行一个整数 N 表示城市的数量。
接下来一行 \(N\) 个整数,第 \(i\) 个表示 \(li\)
接下来一行 \(N\) 个整数,第 \(i\) 个表示 \(ri\)
接下来 \(N-1\) 行,每行三个整数 \(u,v,w\),表示一条道路连接 \(u\)\(v\),权值为\(w\)
输出格式
对于每组数据,如果 \(typ = 0\),则输出一行一个 0(可以满足要求) 或 1(不满足要求)。
如果 \(typ = 1\),则输出一行一个整数表示需要的最小代价。
样例输入
1 1
4
1 3 5 7
2 4 6 8
1 2 1
4
1 3 2
1 4 1
样例输出
2
数据范围与约定
对于 \(20\)% 的数据,\(1 ≤ N ≤ 5,w, |li|, |ri| ≤ 8;\)
对于 \(40\)% 的数据,\(1 ≤ N ≤ 70\);
对于 \(65\)% 的数据,\(1 ≤ N ≤ 1000\);
对于额外 \(15\)% 的数据, 保证 \(w = 0\)
对于 \(100\)% 的数据,保证 \(1 ≤ T ≤ 3\),\(typ ∈ {0or1},1 ≤ N ≤ 10^6,0 ≤ w, |li|, |ri| ≤ 109\)
在所有测试点内,均匀分布着约 \(20\)% 的数据,保证 \(typ = 0\)

思路:

首先发现这道题条件稍显繁琐,考虑将它进行简化
仔细读题,一种差分约束的感觉扑面而来
于是在原树的基础上新设一个点T,
向每个节点连边权为ri的单向边,由每个节点向T连边权为-li的单向边
于是发现是否有解取决于是否存在负环。
typ=0情况解决
typ=1时,不难发现其单调性,二分答案判断即可
至于如何判断负环,可用树形DP或spfa

启发:

差分约束加的边可以与原图结合在一起

p.s. 此题好像可以不用二分,可惜我太菜了。。。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int tot,t,n,top;
int l[N],r[N];
int fi[N],ne[N<<1],to[N<<1],w[N<<1];
int sta[N];
long long d[N];
bool type,insta[N];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
inline void add(int x,int y,int s)
{
	ne[++tot]=fi[x],fi[x]=tot,to[tot]=y,w[tot]=s;
}
bool dfs_spfa(int u,int pos)
{
	sta[++top]=u;insta[u]=true;
	for(int i=fi[u];i;i=ne[i])
	{
		int v=to[i];
		if(d[u]+1ll*w[i]<d[v])
		{
			if(insta[v])return true;
			d[v]=d[u]+1ll*w[i];
			if(dfs_spfa(v,pos))return true;
		}
	}
	--top;insta[u]=false;
	return false;
}
inline bool go(int u,int pos)
{
	for(int i=1;i<=n;++i)
		d[i]=1e15,insta[i]=false;
	d[n+1]=0;
	top=0;
	return dfs_spfa(u,pos);
}
inline bool ck(int pos)
{
	for(int i=1;i<=n;++i)w[i+2*n-2]+=pos;
	for(int i=1;i<=n;++i)w[i+3*n-2]+=pos;
	bool ff=go(n+1,pos);
	for(int i=1;i<=n;++i)w[i+2*n-2]-=pos;
	for(int i=1;i<=n;++i)w[i+3*n-2]-=pos;
	return !ff;
}
inline void clear()
{
	tot=0;
	for(int i=1;i<=n+1;++i)fi[i]=0;
}
int main()
{
//	freopen("teleport.in","r",stdin);
//	freopen("teleport.out","w",stdout);
	t=read(),type=read();
	while(t--)
	{
		n=read();
		bool flag=0;
		for(int i=1;i<=n;++i)l[i]=read();
		for(int i=1;i<=n;++i)r[i]=read();
		for(int i=1;i<n;++i)
		{
			int u=read(),v=read(),_w=read();
			add(u,v,_w),add(v,u,_w);
			if(_w) flag=1;
		}
		int mx=-1e9;
		for(int i=1;i<=n;++i)mx=max(mx,l[i]);
		int mn=1e9;
		for(int i=1;i<=n;++i)mn=min(mn,r[i]);
		if(mx<=mn){puts("0");clear();continue;}
		int ans=mx-mn;
		if(!flag)
		{
			if(!type) puts("1");
			else printf("%d\n",(ans&1)?(ans/2+1):(ans>>1));
			clear();continue;
		}
		for(int i=1;i<=n;++i)add(i,n+1,-l[i]);
		for(int i=1;i<=n;++i)add(n+1,i,r[i]);
		if(!type)
		{
			if(go(n+1,0)) puts("1");
			else puts("0");
			clear();
			continue;
		}
		int ll=0,rr=ans;
		while(ll<rr)
		{
			int mid=ll+rr>>1;
			if(ck(mid)) rr=mid;
			else ll=mid+1;
		}
		printf("%d\n",rr);
		clear();
	}
	return 0;
}

考场代码+改动,有些繁琐

posted @ 2020-01-20 16:43  BILL666  阅读(275)  评论(0编辑  收藏  举报