123789456ye

已AFO

4.7考试总结

A 大逃杀

题面:51nod 2982
题解:本来大概是一道网络流题?
结果变成了一道模拟题(因为方案已经给你了,直接算就行)
顺便说一下,样例有问题

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar(); int f = 1;
	while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); }
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
	x *= f;
}
#define maxn 105
int x[maxn], y[maxn], X[maxn], Y[maxn];
inline int absdec(int x, int y) { return x > y ? x - y : y - x; }
int main()
{
	int n, m, ans = 0;
	read(n), read(m);
	for (int i = 1; i <= n; ++i) read(x[i]), read(y[i]);
	for (int i = 1, tp; i <= m; ++i) read(X[i]), read(Y[i]), read(tp);
	for (int i = 1,num; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
		{
			read(num);
			ans += num * (absdec(x[i], X[j]) + absdec(y[i], Y[j]) + 1);
		}
	printf("%d\n", ans);
	return 0;
}

B 土地划分

题面:51nod 2935
题解:最小割
这个模型还是比较套路吧
分成两遍的话直接上最小割,一边连S一边连T
考虑路的收益:
先假设和S联通的是属于A国的,和T联通的属于B国
则如果我们要使某一条路连结的都属于A国,就意味着要舍弃掉属于B国的部分收益
所以把这条路的两端点向T连边,边权为\(b_i/2\),表示如果都属于A就舍弃了属于\(B\)的这部分收益
如果两端点不属于一个国家怎么办?
此时必定是两个端点都只割了一条边(因为由上面的分析,每条边对应的两个端点都连了两条边)
所以此时已经扣掉了\(a_i/2+b_i/2\),而总的惩罚是\(a_i+b_i+c_i\)
所以两个端点再互联边权为\(a_i/2+b_i/2+c+i\),表示至少要割掉一条边
实现上可以先全部乘2

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxm 800005
#define maxn 20010
#define inf 0x3f3f3f3f
struct Edge
{
	int fr, to, val;
}eg[maxm];
int edgenum = 1, maxflow, head[maxn], cur[maxn];
inline void add(int fr, int to, int val)
{
	eg[++edgenum] = { head[fr],to,val };
	head[fr] = edgenum;
}
int deep[maxn], vis[maxn], S, T;
#define to eg[i].to
#define Add(fr,to,val) add(fr,to,val),add(to,fr,0)
bool bfs()
{
	for (register int i = 0; i <= T; ++i) deep[i] = inf, vis[i] = 0, cur[i] = head[i];
	deep[S] = 0;
	queue<int> q;
	q.push(S);
	while (!q.empty())
	{
		int tmp = q.front();
		q.pop();
		vis[tmp] = 0;
		for (int i = head[tmp]; i; i = eg[i].fr)
			if (deep[to] > deep[tmp] + 1 && eg[i].val)
			{
				deep[to] = deep[tmp] + 1;
				if (!vis[to]) vis[to] = 1, q.push(to);
			}
	}
	if (deep[T] != inf) return 1;
	return 0;
}
int dfs(int now, int flow)
{
	if (now == T)
	{
		maxflow += flow;
		return flow;
	}
	int tmpflow = 0, used = 0;
	for (int& i = cur[now]; i; i = eg[i].fr)
		if (deep[to] == deep[now] + 1 && eg[i].val)
			if (tmpflow = dfs(to, min(flow - used, eg[i].val)))
			{
				used += tmpflow;
				eg[i].val -= tmpflow;
				eg[i ^ 1].val += tmpflow;
				if (used == flow) break;
			}
	return used;
}
inline void dinic() { while (bfs()) dfs(S, inf); }
long long sum;
int main()
{
	int n, m;
	read(n), read(m);
	S = n + 1, T = n + 2;
	for (int i = 2, va; i < n; ++i) read(va), Add(S, i, va << 1), sum += va;
	for (int i = 2, vb; i < n; ++i) read(vb), Add(i, T, vb << 1), sum += vb;
	Add(S, 1, inf), Add(1, T, 0);
	Add(S, n, 0), Add(n, T, inf);
	for (int i = 1, x, y, a, b, c; i <= m; ++i)
	{
		read(x), read(y), read(a), read(b), read(c);
		Add(S, x, a), Add(S, y, a);
		Add(x, T, b), Add(y, T, b);
		Add(x, y, a + b + 2 * c), Add(y, x, a + b + 2 * c);
		sum += a + b;
	}
	dinic();
	printf("%lld\n", sum - (maxflow >> 1));
	return 0;
}

C 城市

题面:51nod 2983
题解:最短路图+DAG支配树
说在前面:\(S\color{red}{angber},yyds!\)
考场上自行推出支配树,这是什么神仙?
再来一遍,\(S\color{red}{angber},yyds!\)


首先考虑建出最短路图
对于一条边,我们考虑化边为点
也就是如果最短路图上有一条边\((x,y)\),新建一个点\(z\),连\((x,z),(z,y)\),这样就转化为了统计必须要经过\(z\)的数量
然后由于最短路图是一个\(DAG\),所以接下来的部分可以参看[ZJOI2012]灾难
一些变量名我会在代码里注释

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 400005
struct Edge { int fr, to, val; };
struct Graph
{
	Edge eg[maxn << 1];
	int head[maxn], edgenum;
	inline void add(int fr, int to, int val = 0)
	{
		eg[++edgenum] = { head[fr],to,val };
		head[fr] = edgenum;
	}
	inline void Add(int fr, int to, int val) { add(fr, to, val), add(to, fr, val); }
}Origin, DAG, Tree;//原图,最短路图(稍微改了一下),支配树
int n, m, s;
int dis[maxn];
#define eg Origin.eg
#define head Origin.head
void dijskra()//求最短路
{
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>q;
	memset(dis, 0x3f, sizeof(dis));
	q.push(make_pair(dis[s] = 0, s));
	while (!q.empty())
	{
		int tp = q.top().second, dist = q.top().first;
		q.pop();
		if (dis[tp] != dist) continue;
		for (int i = head[tp]; i; i = eg[i].fr)
			if (dis[eg[i].to] > dis[tp] + eg[i].val)
				q.push(make_pair(dis[eg[i].to] = dis[tp] + eg[i].val, eg[i].to));
	}
}
int dad[maxn], deg[maxn], fa[maxn][20], dep[maxn];//支配树相关
int tot, id[maxn], sum[maxn], siz[maxn], ans[maxn];//统计答案相关
int Fr[maxn], To[maxn], Val[maxn];//一开始的边
void build_DAG()//建最短路图
{
	tot = n;
	for (int i = 1; i <= m; ++i)
	{
		if (dis[Fr[i]] + Val[i] == dis[To[i]])
		{
			id[++tot] = i;
			DAG.add(Fr[i], tot), ++deg[tot];
			DAG.add(tot, To[i]), ++deg[To[i]];
		}
		if (dis[To[i]] + Val[i] == dis[Fr[i]])
		{
			id[++tot] = i;
			DAG.add(To[i], tot), ++deg[tot];
			DAG.add(tot, Fr[i]), ++deg[Fr[i]];
		}
	}
}
int lca(int x, int y)
{
	if (dep[x] < dep[y]) swap(x, y);
	int dist = dep[x] - dep[y];
	for (int i = 0; i <= 17; ++i)
		if ((1 << i) & dist) x = fa[x][i];
	if (x == y) return x;
	for (int i = 17; i >= 0; --i)
		if (fa[x][i] != fa[y][i])
			x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}
#undef eg
#undef head
#define eg DAG.eg
#define head DAG.head
#define to eg[i].to
void toposort()//建支配树
{
	memset(dad, -1, sizeof(dad));
	queue<int> q;
	for (int i = 1; i <= n; ++i)
		if (!deg[i]) q.push(i), dad[i] = 0;
	while (!q.empty())
	{
		int tp = q.front(); q.pop();
		Tree.add(dad[tp], tp); dep[tp] = dep[dad[tp]] + 1;
		fa[tp][0] = dad[tp];
		for (int i = 1; i <= 17; ++i) fa[tp][i] = fa[fa[tp][i - 1]][i - 1];
		for (int i = head[tp]; i; i = eg[i].fr)
		{
			if (dad[to] == -1) dad[to] = tp;
			else dad[to] = lca(dad[to], tp);
			if (!--deg[to]) q.push(to);
		}
	}
}
#undef eg
#undef head
#undef to
void dfs(int rt)//统计答案,注意新建的点不要算进答案
{
	siz[rt] = (rt <= n);
	for (int i = Tree.head[rt]; i; i = Tree.eg[i].fr)
		dfs(Tree.eg[i].to), siz[rt] += siz[Tree.eg[i].to];
	sum[id[rt]] = siz[rt];//如果是原来就有的点,id[rt]=0,对答案没有影响,否则就代表的是第id[rt]条边
}
int main()
{
	read(n), read(m);
	for (int i = 1; i <= m; ++i)
		read(Fr[i]), read(To[i]), read(Val[i]), Origin.Add(Fr[i], To[i], Val[i]);
	read(s);
	dijskra(); build_DAG();
	toposort(); dfs(0);
	for (int i = 1; i <= m; ++i) ans[Fr[i]] += sum[i], ans[To[i]] += sum[i];
	for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-04-12 20:13  123789456ye  阅读(117)  评论(0编辑  收藏  举报