2022.8.7训练总结

T1.「JOI 2015 Final」JOI 公园

题意分析

题意描述:选择一个自然数xx, 将和第一个广场的距离小于等于xx 的所有广场相互之间连接地下通道

所以,对于给出的每一条边,当max(dis[u], dis[v]) > xmax(dis[u],dis[v])>x时,这条边将额外对答案作出贡献

否则的话,该边所连接的广场均被记入地下通道计划中

所以,对于每个枚举出的xx,它所对应的修缮花费总和为 \sum_1^m∑1m​ didi(max(dis[u], dis[v]) > xmax(dis[u],dis[v])>x) + x * cx∗c


具体实现

1.dijkstradijkstra算法,计算每个广场到一号广场的最短路

2.记录下e[i].sh = max(dis[e[i].u], dis[e[i].v])e[i].sh=max(dis[e[i].u],dis[e[i].v])

3.对ee数组以shsh的大小,由小到大排序

4.暴力枚举每一个分割点,更新最小花费


代码实现

ans = sum;
// sum,最开始记录所有边权的总和
for (int i = 1; i <= m; i ++) {
	e[i].sh = max(dis[e[i].u], dis[e[i].v]);
}
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= m; i ++) {
	sum -= e[i].w;
   // 以第i个边的sh为分割(x),之前的所有边(sh)显然小于等于x
   // 所以这些边的边权全部减去
   // 剩余的sum(边权和)恰好是可以为答案做出贡献的值的总和(即不被纳入地下交通计划)
	ans = min(ans, sum + (long long) e[i].sh *c );
}
printf("%lld\n", ans);

最后提醒:数据范围开long long


T2.「JOI 2017 Final」足球

题意分析

题目难点:如何建图

分析:因为有控球和不控球的状态差别,或者说是有球滚动和人移动的不同代价 所以我们需要拆点(建分层图)

分层

首先,因为人的移动本质上是一样的,所以我们不妨将人带着球的移动当成一层来看

接着,球滚动的过程也需考虑(因为踢球的代价不同)将球的滚动分为两层:横着滚动和竖着滚动(方向不同)

如何从滚动过程跳跃到运球过程

因为状态第一层的定义是人运球的过程,所以从移动过程的当前点(x, y)(x,y)转移至运球过程的当前点(x, y)(x,y)一定需要一名球员来接球

接球的球员一定是距离(x, y)(x,y)最近的那名球员

所以,关于接球球员的选择,我们需要在预处理时求得

另外

坐标是从0开始的,一张图实际共有(n + 1)* (m + 1)(n+1)∗(m+1)个点


以下是具体实现


关于代价

p[sp - 2]p[sp−2], 人的移动

p[sp - 3]p[sp−3],踢球时

p[sp - 4]p[sp−4],球的滚动

变成未被控球看作p[sp - 3]p[sp−3]的代价

滚动一米花费p[sp - 4]p[sp−4]的代价

人在移动,运球时花费p[sp - 2]p[sp−2]的代价

关于拆点

每个点我们拆出 33 个点表示控球(00)、未被控球左右滚动(11)、未被控球上下滚动(22)

关于连边

具体有以下几类边可以相连

(i, j, 0)(i,j,0)向四周的 00 连边权为 CC 的边,表示控球跑 11 米

(i, j, 1)(i,j,1)向左右的 11 连边权为 AA 的边,表示球向左右滚动 11 米

(i, j, 2)(i,j,2)向上下的 22 连边权为 AA 的边,表示球向上下滚动 11 米

(i, j, 0)(i,j,0)向(i, j, 1)(i,j,1)和(i, j, 2)(i,j,2)连边权为 BB 的边,表示把球踢出去

(i, j, 1)(i,j,1)向(i, j, 2)(i,j,2)和(i, j, 0)(i,j,0)连边权为 dis_ij * Cdisi​j∗C 的边,表示最近的球员跑过来控球


我的代码备注

可以看到, id(x, y)id(x,y)是在压缩每一个点的标识,将这张二维图压缩成一维,便于之后的dijkstradijkstra操作

另外,在建边与连边时,参考学习并查集时的扩展域思想

有id(x, y) + (n + 1) * (m + 1)id(x,y)+(n+1)∗(m+1) 和 id(x, y) + 2 * (n + 1) * (m + 1)id(x,y)+2∗(n+1)∗(m+1)的操作


代码实现

以下是完整代码

#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define sc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define pus make_pair
#define pa pair<long long, int>
const int N = 1e5 + 5, M = 505, INF = 0x3f3f3f3f, sp = 5, S = M * M * 30;
const int dx[sp] = { 0, 0, 1, -1 };
const int dy[sp] = { 1, -1, 0, 0 };

int m, n, s, cnt;
long long p[sp];
int head[S / 10], ver[S], nextw[S];
bool v[M][M], vis[S / 10];
long long dis[S / 10], dist[M][M], edgew[S];

struct node {
    int x, y;
};
node st[N];

struct edge {
    long long val;
    int id;
    edge(long long x, int y) {
        val = x;
        id = y;
    }
};

priority_queue<edge> q;

bool operator<(edge x, edge y) { return x.val > y.val; }

inline int id(int x, int y) {
	return x * (m + 1) + y;
}

void add_edge(int x, int y, long long z) {
	ver[++cnt] = y;
	edgew[cnt] = (long long) z;
	nextw[cnt] = head[x];
	head[x] = cnt;
}

void bfs() {
    queue<pa> q;
    for (int i = 1; i <= s; i++) {
        q.push(pus(st[i].x, st[i].y));
        dist[st[i].x][st[i].y] = 0;
    }
    while (q.size()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i], ny = y + dy[i];
            if (nx < 0 || ny < 0 || nx > n || ny > m || v[nx][ny] || dist[nx][ny]) {
                continue;
            }
            dist[nx][ny] = dist[x][y] + 1;
            q.push(pus(nx, ny));
        }
    }
}

void dijkstra(int s) {
	memset(dis, 0x3f, sizeof dis);
    dis[s] = 0;
    q.push(edge(0, s));
    while (!q.empty()) {
        int x = q.top().id;
        q.pop();
        if (vis[x])
            continue;
        vis[x] = 1;
        for (int i = head[x]; i; i = nextw[i]) {
            int y = ver[i];
            long long z = edgew[i];
            if (dis[y] > dis[x] + z) {
                dis[y] = dis[x] + z;
                q.push(edge(dis[y], y));
            }
        }
    }
}

int main() {
    sc;
    cin >> n >> m;
    for (int i = 1; i < 4; i++) {
        cin >> p[i];
    }
    cin >> s;
    for (int i = 1; i <= s; i++) {
        cin >> st[i].x >> st[i].y;
        v[st[i].x][st[i].y] = true;
    }
    bfs();
    for (int x = 0; x <= n; x++) {
    	for (int y = 0; y <= m; y++) {
    		for (int i = 0; i < 4; i++) {
    			int nx = x + dx[i], ny = y + dy[i];
            	if (nx < 0 || ny < 0 || nx > n || ny > m) {
                	continue;
            	}
            	// 向四周的 0 连边权为 C 的边,表示控球跑 1 米
            	add_edge(id(x, y), id(nx, ny), p[sp - 2]);
			}
			// 向(i,j,1)和(i,j,2)连边权为 B 的边,表示把球踢出去
			add_edge(id(x, y), id(x, y) + (n + 1) * (m + 1), p[sp - 3]);
			add_edge(id(x, y), id(x, y) + 2 * (n + 1) * (m + 1), p[sp - 3]);
		} 
	}
	for (int x = 0; x <= n; x++) {
    	for (int y = 0; y <= m; y++) {
    		for (int i = 0; i < 2; i++) {
    			int nx = x + dx[i], ny = y + dy[i];
            	if (nx < 0 || ny < 0 || nx > n || ny > m) {
                	continue;
            	}
            	// 向左右的 1 连边权为 A 的边,表示球向左右滚动 1 米
            	add_edge(id(x, y) + (n + 1) * (m + 1), id(nx, ny) + (n + 1) * (m + 1), p[sp - 4]);
			}
			// 向(i,j,2)和(i,j,0)连边权为 dis_ij * C的边,表示最近的球员跑过来控球
			add_edge(id(x, y) + (n + 1) * (m + 1), id(x, y), dist[x][y] * p[sp - 2]);
		} 
	}
	for (int x = 0; x <= n; x++) {
    	for (int y = 0; y <= m; y++) {
    		for (int i = 2; i < 4; i++) {
    			int nx = x + dx[i], ny = y + dy[i];
            	if (nx < 0 || ny < 0 || nx > n || ny > m) {
                	continue;
            	}
            	// 向上下的 2 连边权为 A 的边,表示球向上下滚动 1 米
            	add_edge(id(x, y) + 2 * (n + 1) * (m + 1), id(nx, ny) + 2 * (n + 1) * (m + 1), p[sp - 4]);
			}
			// 向(i,j,2)和(i,j,0)连边权为 dis_ij * C的边,表示最近的球员跑过来控球
			add_edge(id(x, y) + 2 * (n + 1) * (m + 1), id(x, y), dist[x][y] * p[sp - 2]);
		} 
	}
	dijkstra(id(st[1].x, st[1].y));
	printf("%lld", dis[id(st[s].x, st[s].y)]);

    return 0;
}


草稿——

T3.「JOI 2013 Final」 现代豪宅

题意分析


具体实现



代码实现

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define sc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
const int N = 3000005, M = 40000005, INF = 0x7f7f7f7f7f7f, sp = 5;

int m, n, s, cnt, sum, x, y, p;
int head[N], ver[M], nextw[M], edgew[M];
int dis[N];
bool vis[N];

struct node {
	int x, y;
};
vector<node> h[N], w[N];

bool cmp(node x, node y) {
	return x.x < y.x;
}

struct edge {
	int val, id;
	edge(int x, int y) {
		val = x;
		id = y;
	}
};

priority_queue<edge> q;

bool operator <(edge x, edge y) {
	return x.val > y.val;
}

void add(int x, int y, int z) {
	ver[++sum] = y;
	edgew[sum] = z;
	nextw[sum] = head[x];
	head[x] = sum;
}

void dijkstra() {
	for (int i = 0; i <= p; i ++) {
		dis[i] = INF;
	}
	dis[0] = 0;
	q.push(edge(0, 0));
	while (q.size()) {
		edge u = q.top();
		q.pop();
      // 因为在处理dijkstra时,我们是先更改值,再将对应的标识压入队列
      // 所以,当u.val > dis[u.id]时,代表u.id已被访问过,确定最短路
		if (u.val > dis[u.id]) {
			continue;
		}
		for (int i = head[u.id]; i; i = nextw[i]) {
			int y = ver[i];
			long long z = edgew[i];
			if (dis[y] > dis[u.id] + z) {
				dis[y] = dis[u.id] + z;
				q.push(edge(dis[y], y));
			}
		}
	}
}

signed main()  {
	sc;
	cin >> m >> n >> s;
	p = s << 1 | 1;
	for (int i = 1; i <= s; i ++) {
		cin >> x >> y;
		++ cnt;
		w[x].push_back((node) {y, cnt});
		h[y].push_back((node) {x, cnt + s});
	}
	for (int i = 1; i <= n; i ++) {
		sort(h[i].begin(), h[i].end(), cmp);
		int len = h[i].size();
		for (int j = 1; j < len; j ++) {
			add(h[i][j - 1].y, h[i][j].y, h[i][j].x - h[i][j - 1].x);
			add(h[i][j].y, h[i][j - 1].y, h[i][j].x - h[i][j - 1].x);
		}
	}
	if(h[n].size()) {
		int len = h[n].size() - 1;
		add(p, h[n][len].y, m - h[n][len].x);
		add(h[n][len].y, p, m - h[n][len].x);
	}
	for (int i = 1; i <= m; i ++) {
		sort(w[i].begin(), w[i].end(), cmp);
		int len = w[i].size();
		for (int j = 1; j < len; j ++) {
			add(w[i][j - 1].y, w[i][j].y, w[i][j].x - w[i][j - 1].x);
			add(w[i][j].y, w[i][j - 1].y, w[i][j].x - w[i][j - 1].x);
		}
	}
	if(w[m].size()) {
		int len = w[m].size() - 1;
		add(p, w[m][len].y, n - w[m][len].x);
		add(w[m][len].y, p, n - w[m][len].x);
	}
	if(w[1].size()) {
		add(0, w[1][0].y, w[1][0].x - 1);
		add(w[1][0].y, 0, w[1][0].x - 1);
	}
	for (int i = 1; i <= s; i ++) {
		add(i, i + s, 1);
		add(i + s, i, 1);
	}
	dijkstra();
	if (dis[p] > INF - 1) {
		puts("-1");
		return 0;
	}
	printf("%lld", dis[p]);
	
	
	return 0;
}

T4.「JOISC 2014 Day1」有趣的家庭菜园

题意分析


具体实现


#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define sc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 3e5 + 5, INF = 0x3f3f3f3f, sp = 5;

int n;
long long ans;
int s[N], d[N], BIT[N][2];

int lowbit(int x) { return x & -x; }

void updata(int x, int k, int fl) {
    for (int i = x; i <= n; i += lowbit(i)) {
        BIT[i][fl] += k;
    }
}

int sum(int x, int fl) {
    int nsum = 0;
    for (int i = x; i > 0; i -= lowbit(i)) {
        nsum += BIT[i][fl];
    }
    return nsum;
}

void sf() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i]);
        d[i] = s[i];
    }
}

void discrete() {
    sort(s + 1, s + n + 1);
    int cnt = unique(s + 1, s + n + 1) - s - 1;
    for (int i = 1; i <= n; i++) {
        d[i] = lower_bound(s + 1, s + cnt + 1, d[i]) - s;
        updata(d[i], 1, 1);
    }
}

int main() {
    sf();
    discrete();
    for (int i = 1; i <= n; i++) {
        int u, v;
        updata(d[i], -1, 1);
        u = n - i - sum(d[i], 1);
        updata(d[i], 1, 0);
        v = i - sum(d[i], 0);
        ans += min(u, v);
    }
    printf("%lld", ans);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值