123789456ye

已AFO

3.5考试总结

A 狭窄的通道

题意:51nod 1331
题解:贪心
一种显然的想法是分成左中右三部分,左边退到左边再进去,右边同理,中间直接移,枚举左边和中间分界点
于是你获得了1/25的好成绩

1   
5 11        
5 3     
3 4     
1 10     
7 9     
9 1

这组数据答案是48,做法是先把前三个移到0,后两个移到L,再把1移到L,5移到0,最后左右分别移
所以还要一种贪心,枚举断点,左边全移0,右边全移L,分别按终点排序
然后设\(l,r\)为断点左边,右边的第一个
枚举直到发生冲突(左边的某一个的终点大于右边的)
avater
然后有两种方法

  • 左边的剩余的全部移到L
  • 右边的从上次移到这次移的全部移到0

第一种一次性把所有冲突解决了,直接更新
第二种不一定解决了所有冲突,需要等最后再更新
复杂度:\(O(n^2\log n)\)(应该吧)

#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 105
#define ll long long 
ll ans, sum;
int n, L;
struct Wolf
{
	int s, t;
}w[maxn];
inline bool cmp(Wolf& x, Wolf& y) { return x.s < y.s; }
inline bool cmp2(Wolf& x, Wolf& y) { return x.t < y.t; }
inline bool check(int l1, int r1, int l2, int r2)
{
	int mx = 0, mn = 1e9;
	for (int i = l1; i <= r1; ++i) mx = max(mx, w[i].t);
	for (int i = l2; i <= r2; ++i) mn = min(mn, w[i].t);
	return mx < mn;
}
void solve1()
{
	for (int i = 0; i <= n; ++i)
	{
		if (!check(0, i, i + 1, n)) continue;
		int cut = n + 1; sum = 0;
		for (int j = 0; j <= i; ++j) sum += w[j].s + w[j].t;
		for (int j = i + 1; j <= n; ++j)
		{
			if (!check(i + 1, j, j + 1, n))
			{
				cut = j;
				break;
			}
			sum += abs(w[j].s - w[j].t);
		}
		for (int j = cut; j <= n; ++j) sum += 2 * L - w[j].s - w[j].t;
		ans = min(ans, sum);
	}
}
void calc(int cut)
{
	sort(w + 1, w + cut + 1, cmp2);
	sort(w + cut + 1, w + n + 1, cmp2);
	int l = 1, r = cut + 1, rlast = cut + 1;
	ll sum1 = 0, sum2 = 0;
	while (l <= cut && r <= n)
	{
		sum2 = 0;
		while (l <= cut && w[l].t < w[r].t) ++l;
		if (l == cut + 1) break;
		while (r <= n && w[r].t < w[l].t) ++r;
		for (int i = l; i <= cut; ++i) sum2 += 2 * (L - w[i].t);//左边移L
		ans = min(ans, sum + sum1 + sum2);
		for (int i = rlast; i < r; ++i) sum1 += 2 * w[i].t;//右边移0
		if (w[r].t > w[cut].t) break;
		rlast = r, ++l;
	}
	ans = min(ans, sum + sum1);
}
void solve2()
{
	for (int i = 1; i < n; ++i)
	{
		sort(w + 1, w + n + 1, cmp);
		sum = 0;
		for (int j = 1; j <= i; ++j) sum += w[j].s + w[j].t;
		for (int j = i + 1; j <= n; ++j) sum += 2 * L - w[j].s - w[j].t;
		if (sum >= ans || check(1, i, i + 1, n)) continue;
		calc(i);
	}
}
int main()
{
	int T; read(T);
	while (T--)
	{
		read(n), read(L), ans = 1e18, sum = 0;
		for (int i = 1; i <= n; ++i) read(w[i].s), read(w[i].t);
		w[n + 1].s = w[n + 1].t = L;
		sort(w + 1, w + n + 1, cmp);
		solve1(); solve2();
		printf("%lld\n", ans);
	}
	return 0;
}

B 破坏道路

题意:51nod 1444
题解:最短路
预处理任意两点间的最短路
题意可以转化为至少需要多少条路才能达到要求
于是可以合并道路,具体看代码
注意这道题由于边数很少,dijskra会T掉
复杂度:\(O(n^2+n*spfa)\)

#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 3005
#define mp make_pair
#define pa pair<int,int>
struct Edge
{
	int fr, to, val;
}eg[maxn << 1];
int head[maxn], edgenum, n, m, dis[maxn][maxn], vis[maxn];
int s1, t1, l1, s2, t2, l2;
inline void add(int fr, int to)
{
	eg[++edgenum] = { head[fr],to,1 };
	head[fr] = edgenum;
}
queue<int> q;
inline void spfa(int S)
{
	q.push(S);
	dis[S][S] = 0;
	while (!q.empty())
	{
		int tp = q.front();
		q.pop();
		vis[tp] = 0;
		for (int i = head[tp]; i; i = eg[i].fr)
			if (dis[S][eg[i].to] > dis[S][tp] + eg[i].val)
			{
				dis[S][eg[i].to] = dis[S][tp] + eg[i].val;
				if (!vis[eg[i].to]) vis[eg[i].to] = 1, q.push(eg[i].to);
			}
	}
}
inline void cmin(int& a, int b)
{
	a = min(a, b);
}
int main()
{
	read(n), read(m);
	for (int i = 1, u, v; i <= m; ++i)
		read(u), read(v), add(u, v), add(v, u);
	read(s1), read(t1), read(l1), read(s2), read(t2), read(l2);
	memset(dis, 0x3f, sizeof(dis));
	for (int i = 1; i <= n; ++i) spfa(i);
	if (dis[s1][t1] > l1 || dis[s2][t2] > l2)
	{
		puts("-1");
		return 0;
	}
	int ans = dis[s1][t1] + dis[s2][t2];
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
		{
			if (dis[s1][i] + dis[i][j] + dis[j][t1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][t2] <= l2)
				cmin(ans, dis[s1][i] + dis[s2][i] + dis[i][j] + dis[j][t1] + dis[j][t2]);
			if (dis[s1][j] + dis[j][i] + dis[i][t1] <= l1 && dis[s2][i] + dis[i][j] + dis[j][t2] <= l2)//不写这个就只有85
				cmin(ans, dis[s1][j] + dis[j][i] + dis[i][t1] + dis[s2][i] + dis[j][t2]);
		}
	printf("%d\n", m - ans);
	return 0;
}

C 矩形面积交

题意:51nod 1302
题解:贪心
一组里的面积一定是最短长乘最短宽,一定是短边对短边,长边对长边
钦定最短短边放在A组里
按长边把剩下的排序
长边从大往小扔到B组里,满了就把短边最短的扔到A组里
注意A组的最短长度要特判一下
复杂度:\(O(n\log n)\)

#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 ll long long
#define mp make_pair
#define pa pair<int,int>
priority_queue<pa, vector<pa>, greater<pa> >q;
struct Rectangle
{
	int x, y;
	bool operator < (const Rectangle& p) const
	{
		if (y != p.y) return y < p.y;
		return x < p.x;
	}
}a[200005];
int n;
int main()
{
	read(n);
	for (int i = 1; i <= n * 2; ++i)
	{
		read(a[i].x), read(a[i].y);
		if (a[i].x > a[i].y) swap(a[i].x, a[i].y);
		if (a[i].x < a[1].x || (a[i].x == a[1].x && a[i].y < a[1].y)) swap(a[i], a[1]);
	}
	sort(a + 2, a + 2 * n + 1);
	ll ans = 0;
	for (int i = 2 * n; i >= 2; --i)
	{
		q.push(mp(a[i].x, a[i].y));
		if (q.size() > n)
		{
			a[1].y = min(a[1].y, q.top().second);
			q.pop();
		}
		if (q.size() == n)
		{
			int tmp = a[1].y;
			if (i != 2) tmp = min(tmp, a[2].y);//i=2时已经被分好组了,否则默认A组
			ans = max(ans, 1ll * a[1].x * tmp + 1ll * a[i].y * q.top().first);//A+B
		}
	}
	printf("%lld\n", ans);
	return 0;
}

D 两棵树的问题

题意:51nod 1325
题解:最大权闭合子图
可惜我考场上没想到啊
注意是无根树,所以要枚举根
或者也可以用点分治求出必选的重心这里
复杂度:\(O(n^4)/O(n^3\log n)\)(设最大流复杂度为\(n^3\)

#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 100005
#define inf 0x3f3f3f3f
struct Edge
{
	int fr, to, val;
}eg[maxn << 1];
int head[maxn], edgenum = 1;
inline void add(int fr, int to, int val)
{
	eg[++edgenum] = { head[fr],to,val };
	head[fr] = edgenum;
}
#define Add(a,b,c) add(a,b,c),add(b,a,0)
#define to eg[i].to
int dep[maxn], vis[maxn], cur[maxn], S, T, maxflow;
queue<int> q;
bool bfs()
{
	for (int i = 0; i <= T; ++i) dep[i] = inf, cur[i] = head[i];
	while (!q.empty()) q.pop();
	dep[S] = 0; q.push(S);
	while (!q.empty())
	{
		int tp = q.front();
		q.pop(); vis[tp] = 0;
		for (int i = head[tp]; i; i = eg[i].fr)
			if (dep[to] > dep[tp] + 1 && eg[i].val)
			{
				dep[to] = dep[tp] + 1;
				if (!vis[to]) vis[to] = 1, q.push(to);
			}
	}
	return dep[T] != inf;
}
int dfs(int rt, int flow)
{
	if (rt == T)
	{
		maxflow += flow;
		return flow;
	}
	int tmpflow = 0, used = 0;
	for (int& i = cur[rt]; i; i = eg[i].fr)
		if (dep[to] == dep[rt] + 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);
}
#undef to
int val[maxn];
struct Graph
{
	struct Edge
	{
		int fr, to;
	}eg[maxn << 1];
	int head[maxn], edgenum, fa[maxn];
	inline void add(int fr, int to)
	{
		eg[++edgenum] = { head[fr],to };
		head[fr] = edgenum;
	}
	inline void ADD(int fr, int to)
	{
		add(fr, to), add(to, fr);
	}
	void dfs(int rt)
	{
		for (int i = head[rt]; i; i = eg[i].fr)
			if (eg[i].to != fa[rt]) fa[eg[i].to] = rt, dfs(eg[i].to);
	}
}G[2];
int main()
{
	int n, ans = 0, sum = 0; read(n);
	S = n + 1, T = n + 2;
	for (int i = 1; i <= n; ++i) read(val[i]), sum += val[i] > 0 ? val[i] : 0;
	for (int i = 1, FR, TO; i < n; ++i) read(FR), read(TO), ++FR, ++TO, G[0].ADD(FR, TO);
	for (int i = 1, FR, TO; i < n; ++i) read(FR), read(TO), ++FR, ++TO, G[1].ADD(FR, TO);
	for (int rt = 1; rt <= n; ++rt)
	{
		memset(head, 0, sizeof(head)); edgenum = 1; maxflow = 0;
		for (int i = 1; i <= n; ++i)
		{
			if (val[i] > 0) Add(S, i, val[i]);
			if (val[i] < 0) Add(i, T, -val[i]);
		}
		G[0].fa[rt] = G[1].fa[rt] = 0;
		G[0].dfs(rt), G[1].dfs(rt);
		for (int i = 1; i <= n; ++i)
		{
			Add(i, G[0].fa[i], inf);
			Add(i, G[1].fa[i], inf);
		}
		dinic();
		ans = max(ans, sum - maxflow);
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2020-03-07 17:08  123789456ye  阅读(139)  评论(0编辑  收藏  举报