2022杭电多校5

1003 C Slipper

题意:给定一颗树,起点和终点,树上路径有变权。存在一种特殊的路径当且仅当深度为x的节点可以花费p点代价到深度为x+k或者x-k的节点上,求最短距离

分析:

单源最短路 关键在于建图

想到给每个深度建点 如果每层建立一个点 相差k层的点就能到达

但是此时有个问题 可能会有同层点需要相互到达

怎么办呢?

肯定不能每个点都建边 会超时超内存

每层建立两个点 出点和入点

这样巧妙的解决了同层互相到达的情况!!!

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;
typedef pair<long long, int> PII;
const int N = 4e6 + 10, M = 2 * N , INF = 0x3f3f3f3f;
int h[N], e[M], ne[M], w[M], idx;
long long dist[N];
int depth[N];
vector<int> v[N];
int k, p, S, T, n;
bool st[N];

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u, int fa)
{
	depth[u] = depth[fa] + 1;
	v[depth[u]].push_back(u);
	for(int i = h[u]; ~i ; i = ne[i])
	{
		int j = e[i];
		if(j == fa) continue;
		dfs(j, u);
	}
}

int in(int u)
{
	return u + n;
}

int out(int u)
{
	return u + n * 2;
}

void dijkstra()
{
	priority_queue<PII, vector<PII> ,greater<PII> > q;
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	dist[S] = 0;
	q.push({dist[S], S});

	while(q.size())
	{
		auto t = q.top();
		q.pop();

		int ver = t.second;
		if(st[ver]) continue;
		st[ver] = true;

		for(int i = h[ver]; ~i ; i = ne[i])
		{
			int j = e[i];
			if(dist[j] > dist[ver] + w[i])
			{
				dist[j] = dist[ver] + w[i];
				q.push({dist[j], j});
			}
		}
	}
}

void solve()
{
	memset(h, -1, sizeof h), idx = 0;
	cin >> n;
	for(int i = 1 ; i <= n ; i ++ ) v[i].clear();
	for(int i = 1 ; i < n ; i ++ )
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
		add(b, a, c);
	}
	cin >> k >> p >> S >> T;
	dfs(1, 0);
	for(int i = 1 ; i <= n ; i ++ )
	{
		for(auto item : v[i]) // 深度为i的节点,进入深度为i的管道
		{
			add(item, out(i), 0);
			add(in(i), item, 0);
		}
		if(i - k > 0) add(out(i), in(i - k), p);
		if(i + k <= n) add(out(i), in(i + k), p);
	}

	dijkstra();
	cout << dist[T] << '\n';
}

signed main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ) solve();
	return 0;
}

1010 J Bragging Dice

题意:

喝酒时候的摇骰子游戏,每个人有n个骰子,随机摇到任意点数。某一个人先手他会说某一个数,再说这个数有几个。这个数量代表两个人骰子的数字总和。

如果后手不相信,可以挑战他,开骰。如果数量符合那么挑战者失败,否则挑战成功。

分析:

可以说没有去酒吧玩过这个游戏的人 读懂题目都很难

因为两个人都知道对方的骰子数目,因此其实按照普通规则是先手必胜的。

但是有一个特殊规则,如果一个人手里的骰子的数每个都不一样,则他所有数都为0。

因此如果两个人都出现了这个情况,则一个数都没有。先手不能说0个数,因此他说什么都是输。

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

const int N = 200010;

int n;
int a[N], b[N];
int cnta[6], cntb[6];

void solve() {
    cin >> n;
    for (int i = 1; i <= 6; i++) cnta[i] = cntb[i] = 0;
    bool f = false;
    for (int i = 1; i <= n; i++) cin >> a[i], cnta[a[i]]++;
    for (int i = 1; i <= n; i++) cin >> b[i], cntb[b[i]]++;
    for (int i = 1; i <= 6; i++)
        if (cnta[i] > 1 || cntb[i] > 1) 
            f = true;
    cout << (f ? "Win!\n" : "Just a game of chance.\n");
}

signed main() {
    cin.tie(0)->sync_with_stdio(false);
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}

1012 L Buy Figurines

题意:

排队问题。有n个人来买东西,已知每个人来的时间和购买需要的时间,一共有m个窗口。每个人都会优先选择人少且编号较少的窗口。请问从开始到最后一

个人离开一共花费多久。

分析:

模拟 维护m个窗口的最小值 用到线段树

用优先队列维护一下每个人离开的时间

当轮到一个新的人排队时 只要将已经离开的人减去[线段树单点修改]

每个人离开的时候需要 max(leave[pos],start)+len

表示该人排到pos窗口 但是 不是他想排就马上排到 而是还要保证pos窗口前面的人已经排完

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <cmath>

using namespace std;
#define int long long
#define NO {puts("NO") ; return ;}
#define YES {puts("YES") ; return ;}
#define please return 
#define ac 0
typedef pair<int, int> PII;
const int N = 2e6 + 10 , INF = 0x3f3f3f3f;
int n, m, a[N];
int leave[N];
struct Tree
{
    int l, r;
    int pos, minv; // 排队人最少的位置 / 最少的人数
}tr[N << 2];
struct Node
{
    int start, len;
    bool operator < (const Node &t) const
    {
        return start < t.start; // 按照到达时间排序
    }
}p[N];

void pushup(int u)
{
    tr[u].minv = min(tr[u << 1].minv, tr[u << 1 | 1].minv); // pushup维护pos
    if(tr[u << 1].minv == tr[u].minv) tr[u].pos = tr[u << 1].pos; // 注意 在选择人数少的前提下选择编号较小的
    else tr[u].pos = tr[u << 1 | 1].pos;
}

void build(int u, int l, int r)
{
    tr[u] = {l, r, l, 0}; 
    if(l == r) return ;
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u, int x, int v)
{
    if(x == tr[u].l && x == tr[u].r) 
    {
        tr[u].minv += v;
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(x <= mid) modify(u << 1, x, v);
        if(x > mid) modify(u << 1 | 1, x, v);
        pushup(u);
    }
}

void solve()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i ++ )
    {
        int a, b;
        cin >> a >> b;
        p[i] = {a, b};
    }
    for(int i = 1 ; i <= m ; i ++ ) leave[i] = 0; // 每个人离开的时间
    build(1, 1, m);
    priority_queue<PII, vector<PII> , greater<PII> > q;
    sort(p + 1, p + n + 1);
    int ans = 0;
    for(int i = 1 ; i <= n ; i ++ )
    {
        while(q.size() && q.top().first <= p[i].start)
        {
            modify(1, q.top().second, -1); // 这些人已经离开了
            q.pop();
        }
        int pos = tr[1].pos;
        modify(1, pos, 1); // pos排进一个人
        leave[pos] = max(p[i].start, leave[pos]) + p[i].len; // 计算每个人离开的时间 排队或者直接开始买
        q.push({leave[pos], pos}); // 离开时时间 / 所在窗口
        ans = max(leave[pos], ans); // 更新答案
    }
    cout << ans << endl;
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int T = 1;
    cin >> T;
    while(T -- ) solve();
    please ac;
}
posted @ 2022-09-27 09:36  wzx_believer  阅读(23)  评论(0编辑  收藏  举报