2019.08.17 日常总结

不知这几天种的那好几棵线段树成活了没……

一本通1496:

题意:

在郊区有 NN 座通信基站,PP 条双向电缆,第 ii 条电缆连接基站 AiAi​和 BiBi​​ 。特别地,11 号基站是通信公司的总站,NN 号基站位于一座农场中。现在,农场主希望对通信线路进行升级,其中升级第 ii 条电缆需要花费 LiLi​ 。

电话公司正在举行优惠活动。农场主可以指定一条从 11 号基站到 NN 号基站的路径,并指定路径上不超过 KK 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。

一句话题意:在加权无向图上求出一条从 11 号结点到 NN 号结点的路径,使路径上第 K+1K+1 大的边权尽量小。

思路:满足单调性,所以用二分,把大于mid的边权设为1,小于mid的边权设为0,跑一遍最短路,看看d[n]是否\leq k即可,总体挺水的……

//By hpwwzyy2012
#include <bits/stdc++.h>
using namespace std;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	bool f=0;char c=0;int x=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
const int N=1010;
const int M=2020;
struct node{
	int next,to,w;
}e[M<<1];int h[N],tot;
struct Bian{
	int a,b,w;
	void reading(){
		a=read();b=read();w=read();
	}
}f[M];
inline void add_edge_in_e(int a,int b,int w){
	e[++tot]=(node){h[a],b,w};h[a]=tot;
	e[++tot]=(node){h[b],a,w};h[b]=tot;
}
const int inf=0x3f3f3f3f;
int d[N],n,p,k;bool v[N];
inline void make_a_map(int mid){
	tot=0;memset(h,0,sizeof(h));
	for(int i=1;i<=p;i++){
		if (f[i].w>mid) add_edge_in_e(f[i].a,f[i].b,1);
		else add_edge_in_e(f[i].a,f[i].b,0);
	}
	return;//重新建边,注意内存循环利用(即tot=0)
}
inline bool check(int mid){
	make_a_map(mid);
	for(int i=1;i<=n;i++){
		d[i]=inf;v[i]=true;
	}
	d[1]=0;v[1]=false;
	queue<int> q;q.push(1);
	do{
		int z=q.front();q.pop();v[z]=true;
		for(int i=h[z];i;i=e[i].next){
			register int to=e[i].to;
			if (d[to]>d[z]+e[i].w){
				d[to]=d[z]+e[i].w;
				if (v[to]==true){
					v[to]=false;
					q.push(to);
				}
			}
		}
	}while (q.size());
	return d[n]<=k;
}
//用spfa跑最短路
int l,r,mid,ans,i;
int main(){
//	freopen("t1.in","r",stdin);
	n=read();p=read();k=read();
	for(i=1;i<=p;i++)
	f[i].reading();
	r=-inf;ans=-1;
	for(i=1;i<=p;i++){
//		l=min(l,f[i].w);
		r=max(r,f[i].w);
	}
//	if (!check(r)){
//		printf("-1");
//		return 0;
//	}
	while (l<=r){
		mid=(l+r)>>1;
		if (check(mid)){
			ans=mid;r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d",ans);
	return 0;
}

一本通1512:

题意:

当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。FJ 有 NN 头奶牛,编号从 11 到 NN,沿一条直线站着等候喂食。奶牛排在队伍中的顺序和它们的编号是相同的。因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。即使说,如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 LL。另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 DD。

给出 MLML 条关于两头奶牛间有好感的描述,再给出 MDMD 条关于两头奶牛间存有反感的描述。你的工作是:如果不存在满足要求的方案,输出 −1−1;如果 11 号奶牛和 NN 号奶牛间的距离可以任意大,输出 −2−2;否则,计算出在满足所有要求的情况下,11 号奶牛和 NN 号奶牛间可能的最大距离。

思路:差分约束系统+spfa判负环,思维难道不高,但代码有一定难度。

做法:用边{a,b,c}表示b的位置-a的位置要\geq c

//By hpwwzyy2012
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e3+1e2;
const int M=1e4+1e2;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	bool f=0;char c=0;int x=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
struct node{
	int next,to,w;
}e[(N+M)<<1];int h[N],tot;
//const int inf=0x3f3f3f3f;
inline void add(int a,int b,int c){
	e[++tot]=(node){h[a],b,c};h[a]=tot;
}
ll d[N],n,cnt[N],inf;bool v[N];
inline bool spfa(int s){
	memset(d,127,sizeof(d));
	memset(cnt,0,sizeof(cnt));
	memset(v,true,sizeof(v));
	inf=d[0];d[s]=0;v[s]=false;
	queue<int> q;q.push(s);
	do{
		int z=q.front();q.pop();v[z]=true;
		for(int i=h[z];i;i=e[i].next){
			register int to=e[i].to;
			if (d[to]>d[z]+e[i].w){
				d[to]=d[z]+e[i].w;
				cnt[to]=cnt[z]+1;
				if (cnt[to]>=n) return false;
				if (v[to]){
					v[to]=false;
					q.push(to);
				}
			}
		}
	}while (q.size());
	return true;
}
int m,q,a,b,c,i;
int main(){
//	freopen("t1.in","r",stdin);
	n=read();m=read();q=read();
	for(i=1;i<n;i++) add(i+1,i,0);
    //注意题目暗含的限制,免得跑出一些奇奇怪怪的解
	for(i=1;i<=m;i++){
		a=read();b=read();
		c=read();add(a,b,c);
	}
	for(i=1;i<=q;i++){
		a=read();b=read();
		c=read();add(b,a,-c);
	}
	for(i=1;i<=n;i++) add(0,i,0);
    //为spfa准备
	if (spfa(0)==false) cout<<-1;
	else{
		spfa(1);
		if (d[n]==inf) cout<<-2;
		else cout<<d[n];
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值