2022杭电多校(1)

2022杭电多校(1)

K

签到

B(枚举、BFS/DFS)

由于数据范围很小,可以枚举存在的墙的状态, 对于每种状态,做一遍 bfs,看是否能搜到终点

由于点的坐标不是整数,边的坐标是整数,可以将坐标全部乘 2,这样判断两个点能否通过时不用考虑边界情况,做两次跨立实验即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;


const int N = 16;
int n, m, k;
int sx, sy, ex, ey, state;
bool st[N*2][N*2];
int dx[] = {2, 0, -2, 0};
int dy[] = {0, 2, 0, -2};

struct Wall
{
	int x1, y1, x2, y2;
}a[N];

bool check(int x, int y, int tx, int ty)
{
	if (tx < 0 || tx > 2 * n || ty < 0 || ty > 2 * m)
		return false;
	if (st[tx][ty])
		return false;
	for (int i = 0; i < k; i++)
	{
		if (!(state >> i & 1))
			continue;
		int x1 = a[i].x1, y1 = a[i].y1, x2 = a[i].x2, y2 = a[i].y2;
		if (x == tx && y1 == y2)
		{
			if ((x - x1) * (x - x2) < 0 && (y1 - y) * (y1 - ty) < 0)
				return false;
		}
		if (y == ty && x1 == x2)
		{
			if ((x1 - x) * (x1 - tx) < 0 && (y - y1) * (y - y2) < 0)
				return false;
		}
	}
	return true;
}
bool judge()
{
	memset(st, false, sizeof st);
	queue<PII> q;
	q.push({sx, sy});
	st[sx][sy] = true;
	while(!q.empty())
	{
		int x = q.front().first, y = q.front().second;
		q.pop();
		if (x == ex && y == ey)
			return true;
		for (int i = 0; i < 4; i++)
		{
			int tx = x + dx[i];
			int ty = y + dy[i];
			if (check(x, y, tx, ty))
			{
				st[tx][ty] = true;
				q.push({tx, ty});
			}
		}
	}
	return false;
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> m >> k;
		cin >> sx >> sy >> ex >> ey;
		sx = sx * 2 + 1, sy = sy * 2 + 1, ex = ex * 2 + 1, ey = ey * 2 + 1;
		for (int i = 0; i < k; i++)
		{
			int x1, y1, x2, y2;
			cin >> x1 >> y1 >> x2 >> y2;
			x1 *= 2, y1 *= 2, x2 *= 2, y2 *= 2;
			a[i] = {x1, y1, x2, y2};
		}
		int ans = 0;
		for (state = 0; state < 1 << k; state++)
		{
			int cnt = __builtin_popcount(state);
			if (ans >= cnt)
				continue;
			if (judge())
				ans = cnt;
		}
		cout << k - ans << endl;
	}
    return 0;
}

L(博弈论)

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

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 1e6 + 10;
int n;
int a[N];
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d", &n);
		for (int i = 0; i <= n; i++)
			scanf("%d", a + i);
		for (int i = n; i > 0; i--)
			a[i-1] += a[i] / 2;
		a[0] ? puts("Alice") : puts("Bob");
	}
    return 0;
}

I(计算几何)

  1. 对于第一个点,肯定在四条线之一上,枚举这四种线,记为线1
  2. 对于其余随意一个不在线1上的点,只能在另外三种线上,枚举这三种线,记为线2
  3. 线1、线2固定后,交点即为激光发射点,检查其余 n - 2 个点能否被照到即可

快读!!!

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

#define endl '\n'
using namespace std;
typedef std::pair<int, int> PII;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
int n, k;

int _read() {
	static int ans, c, p;
	for (c = getchar(); c != '-' && (c < '0' || c > '9'); c = getchar());
	if (c == '-') p = false, c = getchar(); else p = true;
	for (ans = 0; c <= '9' && c >= '0'; c = getchar()) ans = ans * 10 + c - '0';
	return p ? ans : -ans;
}

struct Point
{
	int x, y;
	Point operator+(const Point &a) const {return {x + a.x, y + a.y};}
	Point operator-(const Point &a) const {return {x - a.x, y - a.y};}
	Point operator-() const {return {-x, -y};}
	Point operator*(const Point &a) const {return {x * a.x, y * a.y};}
	Point operator*(const double k) const {return {k * x, k * y};}
	Point operator/(const double k) const {return {x / k, y / k};}
	double operator^(const Point &a) const {return x * a.y - y * a.x;}
}p[N];

typedef Point Vector;

Point get_intersection(Point P, Vector v, Point Q, Vector w)
{
	Vector u = P - Q;
	double t = (w ^ u) / (v ^ w);
	return P + v * t;
}

Vector d[4] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}};

bool check(int x, int y)
{
	if (abs(x) == abs(y))
		return true;
	if (x * y == 0)
		return true;
	return false;
}

bool judge(Point c)
{
	for (int i = 2; i <= n; i++)
	{
		if (i == k)
			continue;
		int dx = p[i].x - c.x, dy = p[i].y - c.y;
		// cout << dx << " " << dy << endl;
		if (!check(dx, dy))
			return false;
	}
	return true;
}
bool solve()
{
	for (int i = 0; i < 4; i++)
	{
		for (k = 2; k <= n; k++)
		{
			if (((p[k] - p[1]) ^ d[i]) != 0)
				break;
		}
		if (k == n + 1)
			return true;
		for (int j = 0; j < 4; j++)
		{
			if (i == j)
				continue;
			Point c = get_intersection(p[1], d[i], p[k], d[j]);
			if (judge(c))
				return true;
		}
	}
	return false;
}
int main()
{
	int T;
	T = _read();
	while(T--)
	{
		n = _read();
		for (int i = 1; i <= n; i++)
			p[i].x = _read() * 2, p[i].y = _read() * 2;
		cout << (solve() ? "YES" : "NO") << endl;
	}
	return 0;
}

C(bitset优化dp)

  1. 首先容易想到朴素的dp,\(f[i][j][k]\) 为前 i 个物品,价值为 j ,体积为 k ,能否得到这种状态

    f[i][j][k] = f[i-1][j][k] | f[i-1][j^w][k-v];
    

    复杂度为 \(2^{30}\)

  2. 由于是 bool 型的 dp,可以用 bitset 来优化,用 bitset 存第三维,第 t 位为 1 表示体积为 t 时是可以得到的

  3. 滚动数组优化掉第一维的空间

复杂度为 \(\frac {2^{30}}{64}=2^{24}\)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <bitset>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;


const int N = 1.1e3;
int n, m;
bitset<N> f[N], tmp[N];
int solve()
{
	for (int i = 0; i < N; i++)
			f[i].reset();
		f[0][0] = 1;
		for (int i = 1; i <= n; i++)
		{
			int v, w;
			cin >> v >> w;
			for (int j = 0; j < 1024; j++)
				tmp[j] = f[j] << v;
			for (int j = 0; j < 1024; j++)
				f[j] |= tmp[j^w];
		}
		for (int j = 1023; j >= 0; j--)
		{
			if (f[j][m] == 1)
				return j;
		}
		return -1;
}
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> m;
		cout << solve() << endl;
	}
    return 0;
}

D (bitset优化,扫描线思想)

  1. 筛质数

  2. 求出每两个点的距离,存下这些边,并按距离从小到大的顺序排序

  3. 从小到大枚举边,如果这个边的长度 w 是质数,则求以 w 为中位数的三元组数,为对答案的贡献

  4. 可维护一个bool数组, \(f[i][j]=1\) 表示 (i, j) 的距离小于 w

    设 w 这条边的两端点是 x,y,设第三个点是 k,要满足 w 在 dist(x, k) 与 dist(y, k) 之间,则需要 \(f[x][k] \;xor\; f[y][k]=1\), 求共有多少个这样的 k

  5. 这样枚举仍是 \(n^3\) 的复杂度,由于是 bool 类型的数组,可用 bitset 代替来优化,对答案的贡献就是

    (f[x] ^ f[y]).count()
    

    计算为这条边的贡献后,要更新 bitset,即 \(f[x][y]=f[y][x]=1\)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <bitset>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2e3 + 10, M = 2e5 + 10;
int n, m;

struct Edge
{
	int u, v, w;
	bool operator<(const Edge &x) const
	{
		return w < x.w;
	}
}e[N*N];
struct Point
{
	int x, y;
}p[N];
bitset<N> f[N];
bool st[M];
int pr[M], cnt;
void get_primes(int n)
{
	memset(st, true, sizeof st);
	st[1] = false;
	for (int i = 2; i <= n; i++)
	{
		if (st[i])
			pr[++cnt] = i;
		for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
		{
			int p = pr[j];
			st[i * p] = false;
			if (i % p == 0)
				break;
		}
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	get_primes(2e5 + 10);
	while(T--)
	{
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
			f[i].reset();
		for (int i = 1; i <= n; i++)
			scanf("%d%d", &p[i].x, &p[i].y);
		int tot = 0;
		for (int i = 1; i <= n; i++)
			for (int j = i + 1; j <= n; j++)
				e[++tot] = {i, j, abs(p[i].x - p[j].x) + abs(p[i].y - p[j].y)};
		sort(e + 1, e + tot + 1);
		ll ans = 0;
		for (int i = 1; i <= tot; i++)
		{
			int x = e[i].u, y = e[i].v, w = e[i].w;
			if (st[w])
				ans += (f[x] ^ f[y]).count();
			f[x][y] = f[y][x] = 1;
		}
		printf("%lld\n", ans);
	}
    return 0;
}

H(分层图最短路 + set)

\(dist[i][0/1]\) 表示是否通过特殊边到达第 i 号点的最短距离

分层图最短路即可

但因为走过特殊边后,就可以走向所有点,如果不加优化的话,每个 \(dist[u][1]\) 要更新 n - 1 个点,免费边有 \(n^2\) 个,总复杂度为 \(n^2log m\)

要发现一个性质来优化:走过特殊边后,u 走向非邻点 v 的边的长度是 0,所以用 u 更新 v 时,\(dist[v][0]=dist[u][1]+0\)

若 v 被 u 之后的点 t 更新,则 \(dist[v][0]=dist[t][0/1]+w\), 因为优先队列的性质,并且每次放进去的都比之前再队列里的数大,所以 \(dist[t][0/1]>=dist[u][1]\;且\;w>=0\), 所以如果 u 更新了非邻点 v 后, v 就已经达到了最短距离, 之后再有免费通过的机会就不用考虑 v 了

所以可用 set 记录哪些点还没被上述情况找到最短路,已经找到了就从 set 中删除,因此最多用到更新 \(n\) 次免费边

#include <iostrea
m>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <queue>
using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

int _read() {
	static int ans, c, p;
	for (c = getchar(); c != '-' && (c < '0' || c > '9'); c = getchar());
	if (c == '-') p = false, c = getchar(); else p = true;
	for (ans = 0; c <= '9' && c >= '0'; c = getchar()) ans = ans * 10 + c - '0';
	return p ? ans : -ans;
}

void _write(int ans) {
	static int a[20], n;
	if (ans < 0) {
		putchar('-');
		ans = -ans;
	}
	if (ans == 0) {
		putchar('0');
		return;
	}
	for (n = 0; ans; ans /= 10) a[n++] = ans % 10;
	for (n--; n >= 0; n--) putchar(a[n] + '0');
	return;
}
const int N = 1e6 + 10;
const ll INF = 1e18;
int n, m, s, k;

struct Edge
{
	int v, w, op;
}e[N];
ll dist[N][2];
bool st[N][2], neb[N];//这个点是否更新过其他点;当前点的邻点
struct Node
{
	int id, d, op;
	bool operator<(const Node &x) const
	{
		return d > x.d;
	}
};
vector<vector<Edge> > G(N);
void add(int u, int v, int w, int op)
{
	G[u].push_back({v, w, op});
}

void init()
{
	for (int i = 1; i <= n; i++)
		G[i].clear();
}
void dijkstra(int x)
{
	//还没有确定最短距离的点的集合
	set<int> S;
	for (int i = 1; i <= n; i++)
	{
		dist[i][0] = dist[i][1] = INF;
		st[i][0] = st[i][1] = false;
		if (i != x)
			S.insert(i);
		neb[i] = false;
	}
	dist[x][0] = 0;
	priority_queue<Node> q;
	q.push({x, 0, 0});
	while(!q.empty())
	{
		auto t = q.top();
		q.pop();
		int u = t.id;
		if (t.op == 0)
			S.erase(u);
		else
		{
			//找到当前点的邻点
			for (auto i : G[u])
				neb[i.v] = true;
			vector<int> tmp;
			for (auto i : S)
			{
				if (st[i])
					continue;
				//i还没确定最短距离,因此dist[u][1]就是它的最短距离
				dist[i][0] = dist[u][1];
				q.push({i, dist[i][0], 0});
				tmp.push_back(i);
			}
			//剪枝,因为是堆优化dijkstra,且当前边为 0,之后不会有点能更新i的最小距离了
			for (auto i : tmp)
				S.erase(i);
			//清空,防止影响下一个点的信息
			for (auto i : G[u])
				neb[i.v] = false;
		}
		int sub = (t.op ? k : 0);
		if (st[u][t.op])
			continue;
		st[u][t.op] = true;
		for (auto i : G[u])
		{
			int v = i.v;
			if (dist[v][i.op] > dist[u][t.op] + i.w - sub)
			{
				dist[v][i.op] = dist[u][t.op] + i.w - sub;
				q.push({v, dist[v][i.op], i.op});
			}
		}
	}
}
int main()
{
	int T = _read();
	while(T--)
	{
		n = _read();
		m = _read();
		s = _read();
		k = _read();
		init();
		while(m--)
		{
			int u, v, w, op;
			u = _read();
			v = _read();
			w = _read();
			op = _read();
			add(u, v, w, op);
		}
		dijkstra(s);
		for (int i = 1; i <= n; i++)
		{
			if (min(dist[i][0], dist[i][1]) == INF)
				printf("-1 ");
			else
				printf("%lld ", min(dist[i][0], dist[i][1]));
		}
		puts("");
	}
    return 0;
}
posted @ 2022-07-22 14:14  hzy0227  阅读(114)  评论(0编辑  收藏  举报