线段树/Dijkstra(Legacy CodeForces - 787D )

题目链接
神仙题,马上把本憨批搞死的那种。我一开始想的是dijkstra套线段树。一个点代表一个区间,用区间查询来得出该点的dist值。结果完美的过了2个test(两个样例),然后wa在第3个test上。想到是因为区间与单点之间的转移问题,然后就没有然后了。
看题解真的给大佬跪了orz。打死我也想不到啊。我们先建一个长度n的线段树。然后建立两组节点in[i],out[i],线段树上的每一个节点分配一个in编号,一个out编号。一个(in)建边指向自己的两个孩子,一个(out)指向自己的祖先。权值均为0,然后,在此基础上再改咋建图,咋建图。最后跑一边dijkstra。
下面是ac代码:

// I am just a ping fan ren
//not a god, thanks.
#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <algorithm>
#include <cstdlib>
#define ll long long
using namespace std;
const int N = 1e6 + 5;
const ll inf = 1e16;
int he[N], ver[N << 1], ne[N << 1];
bool vis[N];
ll e[N<<1];
ll dis[N];
int in[N<<1], out[N<<1];
int tot, n, m, s;
priority_queue<pair<ll, int> > q;
struct Node
{
	int l, r;
}tr[N << 2];
void add(int x, int y, ll w)
{
	ver[++tot] = y;
	ne[tot] = he[x];
	e[tot] = w;
	he[x] = tot;
}
int cnt = 1;
void build(int p, int l, int r)
{
	tr[p].l = l; tr[p].r = r;
	if (l == r)
	{
		in[p] = out[p] = l;
		return;
	}
	int mid = (l + r) >> 1;
	in[p] = ++cnt;
	out[p] = ++cnt;
	build(p << 1, l, mid);
	build(p << 1 | 1, mid + 1, r);
	add(in[p], in[p << 1], 0);
	add(in[p], in[p << 1 | 1], 0);
	add(out[p << 1], out[p], 0);
	add(out[p << 1 | 1], out[p], 0);
}
void buildeg(int p, int k, int l, int r, ll w, bool flag)
{
	if (l <= tr[p].l && tr[p].r <= r)
	{
		if (flag) add(out[p], k, w);
		else add(k, in[p], w);
		return;
	}
	int mid = (tr[p].l + tr[p].r) >> 1;
	if (l <= mid) buildeg(p << 1, k, l, r, w, flag);
	if (r > mid) buildeg(p << 1 | 1, k, l, r, w, flag);
}
void dij()
{
	for (int i = 0; i <= cnt; i++) dis[i] = inf;
	dis[s] = 0;
	q.push(make_pair(0, s));
	while (q.size())
	{
		int x = q.top().second;
		q.pop();
		if (vis[x]) continue;
		vis[x] = 1;
		for (int i = he[x]; i; i = ne[i])
		{
			int y = ver[i];
			if (dis[y] > dis[x] + e[i])
			{
				dis[y] = dis[x] + e[i];
				q.push(make_pair(-dis[y], y));
			}
		}

	}
}
int main()
{
	scanf("%d%d%d", &n, &m, &s);
	cnt = n;
	build(1, 1, n);
	while (m--)
	{
		int op;
		scanf("%d", &op);
		int x, y, z;
		ll w;
		if (op == 1)
		{
			scanf("%d%d%lld", &x, &y, &z);
			add(x, y, z);
		}
		else if (op == 2)
		{
			scanf("%d%d%d%lld", &x, &y, &z, &w);
			buildeg(1, x, y, z, w, 0);
		}
		else
		{
			scanf("%d%d%d%lld", &x, &y, &z, &w);
			buildeg(1, x, y, z, w, 1);
		}
	}
	dij();
	for (int i = 1; i <= n; i++)
	{
		if (dis[i] == inf) printf("-1");
		else printf("%lld", dis[i]);
		if (i != n) printf(" ");
	}
	printf("\n");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值