杭电多校2020第四场

Deliver the Cake

题意:

n个地点,m条双向道路,地点的类型分L、R、M三种,到达L点需保持状态L,到达R需保持状态R,到M两种状态都可以,切换状态需要消耗时间x,问s到t点所需最少时间。

题解:

将M类型的点一分为二,1 ~ N所有类型为M的点设为L点,N+1 ~ 2N所有类型为M的点设为R点,那么在加边的时候有7种情况:

(1) u 为 L 并且 v 为 R 或者 u 为 R 并且 v 为 L,那么 u 到 v 的距离为 w + x。
(2) u 为 L 并且 v 为 L 或者 u 为 R 并且 v 为 R,那么 u 到 v 的距离为 w。
(3) u 为 L 并且 v 为 M,那么 u 到 v 的距离为 w,u 到 v + n 的距离为 w + x。
(4) u 为 R 并且 v 为 M,那么 u 到 v 的距离为 w + x,u 到 v + n 的距离为 w。
(5) u 为 M 并且 v 为 L,那么 u 到 v 的距离为 w,u + n 到 v 的距离为 w + x。
(6) u 为 M 并且 v 为 R,那么 u 到 v 的距离为 w + x,u + n 到 v 的距离为 w。
(7) u,v 同时为 M,那么 u 到 v 的距离为 w,u + n 到 v + n 的距离为w,u + n 到 v 的距离为 w + x,u 到 v + n 的距离为w + x。

如果起点为M,只需添加一条权值为0,从s到s+n的边即可。

AC_CODE:

const int maxn = 2e5+5;
const int M = 2e6+5;
ll n,m,s,t,x,cnt;
char tt[maxn];
struct node{
	int to;
	int next;
	ll w;
	bool operator<(const node&a) const{
		return a.w < w;
	}
}edge[M],Now,Next;
int head[maxn];

void init(){
	FOR(i,0,maxn-2) head[i] = -1;
	cnt = 0;
}

void add(int u, int v, ll w){
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].w = w;
	head[u] = cnt; 
}

bool vis[maxn];
ll dis[maxn];

void dijkstra(){
	FOR(i,0,maxn) vis[i] = 0;
	FOR(i,0,maxn) dis[i] = 1LL<<61LL;
	Now.to = s;
	dis[s] = 0;
	priority_queue<node>pq;
	pq.push(Now);
	while(!pq.empty()){
		Now = pq.top();
		pq.pop();
		int now = Now.to;
		if(vis[now]) continue;
		vis[now] = 1;
		for(int i=head[now]; i!=-1; i=edge[i].next){
			int u = edge[i].to;
			if(!vis[u] && dis[u] > dis[now] + edge[i].w){
				dis[u] = dis[now] + edge[i].w;
				Next.to = u;
				Next.w = dis[u];
				pq.push(Next);
			}
		}
	}
	W(min(dis[t],dis[t+n]));
}



void solve(){
	init();
	R(n,m,s,t,x);
	scanf("%s",tt+1);
	if(tt[s] == 'M') add(s,s+n,0),add(s+n,s,0);
	while(m--) {
		ll u,v,w;
		R(u,v,w);
		if((tt[u] == 'L' && tt[v] == 'R') || (tt[u] == 'R' && tt[v] == 'L')) {
			add(u,v,w+x); add(v,u,w+x);
		} else if((tt[u] == 'L' && tt[v] == 'L') || (tt[u] == 'R' && tt[v] == 'R')) {
			add(u,v,w); add(v,u,w);
		} else if(tt[u] == 'L' && tt[v] == 'M') {
			add(u,v,w); add(v,u,w);
			add(u,v+n,w+x); add(v+n,u,w+x);
		} else if(tt[u] == 'R' && tt[v] == 'M') {
			add(u,v,w+x); add(v,u,w+x);
			add(u,v+n,w); add(v+n,u,w);
		} else if(tt[u] == 'M' && tt[v] == 'L') {
			add(u,v,w); add(v,u,w);
			add(u+n,v,w+x); add(v,u+n,w+x);
		} else if(tt[u] == 'M' && tt[v] == 'R') {
			add(u,v,w+x); add(v,u,w+x);
			add(u+n,v,w); add(v,u+n,w);
		} else {
			add(u,v,w); add(v,u,w);
			add(u+n,v,w+x); add(v,u+n,w+x);
			add(u,v+n,w+x); add(v+n,u,w+x);
			add(u+n,v+n,w); add(v+n,u+n,w);
		}
	}
	dijkstra();
}

Last Problem

题意:
给定一个无限大的板子,可以给整数点染色,有n种不同的颜色,前4种可以任意涂,若想涂第i(i>4)种颜色,那么需要满足它的欧几里得距离为1的4个点必须刚好是i-1,i-2,i-3,i-4,输出画出第n种颜色的画法。

题解:
若想画出颜色n,必先画出其上下左右四个点,如下图:
在这里插入图片描述
按照这种思想继续往外扩展:
在这里插入图片描述
dfs从n开始搜索,注意剪枝。

AC_CODE:

const int maxn = 2e2+5;
const int Move[4][2] = {{0,1},{-1,0},{1,0},{0,-1}};
int m[maxn][maxn];

void dfs(int x, int y, int n) {
	if(n < 1) return;
	for(int i=0; i<4; ++i) {
		int dx = x + Move[i][0];
		int dy = y + Move[i][1];
		if(m[dx][dy] != n - i - 1)
			dfs(dx,dy,n-i-1);
	}
	m[x][y] = n;
	W(x,y,n);
}

void solve(){
	int n;
	R(n);
	dfs(100,100,n);
}

Go Running

题意:
有一条无限的跑道,有若干个人在跑道上以1m/s的速度匀速奔跑,要么往跑道的正方向,要么往跑道的反方向,方向一旦确定不可更改,现在给出t时刻下x位置至少有一人,问在跑道上最少有几人。

题解:
绘制一个二维坐标轴t-x,对于每个点的人,对应两条不同的奔跑路线,方向相反。
在这里插入图片描述
把所有点对应的直线画出来,问题可转换为求最少的直线覆盖所有的点,把斜率为1的直线放入U集合,斜率为-1放入V集合,两直线相交的点视为U,V集合之间的连线,即二分图最小顶点覆盖,而最小定点覆盖等于二分图的最大匹配。每个点的坐标为(ti ,xi ),那么斜率为1的直线与Y轴的点纵坐标为xi-ti,斜率为-1的直线与Y轴的点纵坐标为xi+ti,把这两个点放入U、V集合,用dinic求解即可。

下面是建图过程:

设S=4n+1(起点),T=4n+2(终点),1~2n存放U集合的点,2n+1~4n存放V集合的点,S到U集合、U集合到V集合、V集合到T建立容量为1的边,跑最大流即可。

AC_CODE:

const int INF=0x3f3f3f3f;
const int MAXN=6e5+5;//点数的最大值
const int MAXM=1e6+5;//边数的最大值

struct Node
{
	int from,to,next;
	int cap;
}edge[MAXM];
int tol;

int dep[MAXN];//dep为点的层次
int head[MAXN];

int n;
void init()
{
	tol=0;
//	memset(head,-1,sizeof(head));
	for(int i=0; i<=5*n; ++i) head[i] = -1;
}
void addedge(int u,int v,int w)//第一条变下标必须为偶数
{
	edge[tol].from=u;
	edge[tol].to=v;
	edge[tol].cap=w;
	edge[tol].next=head[u];
	head[u]=tol++;
	edge[tol].from=v;
	edge[tol].to=u;
	edge[tol].cap=0;
	edge[tol].next=head[v];
	head[v]=tol++;
}

int BFS(int start,int end)
{
	int que[MAXN];
	int front,rear;
	front=rear=0;
	memset(dep,-1,sizeof(dep));
	que[rear++]=start;
	dep[start]=0;
	while(front!=rear)
	{
		int u=que[front++];
		if(front==MAXN)front=0;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			if(edge[i].cap>0&&dep[v]==-1)
			{
				dep[v]=dep[u]+1;
				que[rear++]=v;
				if(rear>=MAXN)rear=0;
				if(v==end)return 1;
			}
		}
	}
	return 0;
}
int dinic(int start,int end)
{
	int res=0;
	int top;
	int stack[MAXN];//stack为栈,存储当前增广路
	int cur[MAXN];//存储当前点的后继
	while(BFS(start,end))
	{
		memcpy(cur,head,sizeof(head));
		int u=start;
		top=0;
		while(1)
		{
			if(u==end)
			{
				int min=INF;
				int loc;
				for(int i=0;i<top;i++)
					if(min>edge[stack[i]].cap)
					{
						min=edge[stack[i]].cap;
						loc=i;
					}
				for(int i=0;i<top;i++)
				{
					edge[stack[i]].cap-=min;
					edge[stack[i]^1].cap+=min;
				}
				res+=min;
				top=loc;
				u=edge[stack[top]].from;
			}
			for(int i=cur[u];i!=-1;cur[u]=i=edge[i].next)
				if(edge[i].cap!=0&&dep[u]+1==dep[edge[i].to])
					break;
			if(cur[u]!=-1)
			{
				stack[top++]=cur[u];
				u=edge[cur[u]].to;
			}
			else
			{
				if(top==0)break;
				dep[u]=-1;
				u=edge[stack[--top]].from;
			}
		}
	}
	return res;
}

int t[MAXN],x[MAXN];

void solve(){
	R(n);
	VI v;
	for(int i=1; i<=n; ++i) {
		R(t[i],x[i]);
		v.PB(x[i]-t[i]);
		v.PB(x[i]+t[i]);
	}
	init();
	int m = 2 * n;
	int S = 2 * m + 1, T = 2 * m + 2;
	sort(ALL(v));
	int cnt = unique(ALL(v))-v.begin();
	for(int i=1; i<=n; ++i) {
		int a = lower_bound(v.begin(), v.begin()+cnt, (x[i]-t[i])) - v.begin() + 1;
		int b = lower_bound(v.begin(), v.begin()+cnt, (x[i]+t[i])) - v.begin() + 1;
		addedge(a,b+m,1);
	}
	for(int i=1; i<=m; ++i) {
		addedge(S,i,1);
		addedge(i+m,T,1);
	}
	W(dinic(S,T));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值