[赛记] csp-s模拟3

奇观 55pts

赛时打的 Θ(n5)m=0 的特殊性质拿了55pts;

考虑正解,首先,CCF 这三个字母是可以分开维护的;

对于 C,其可以看作一个连了四个点的线段,对于 F,其可以看作一个连了三个点的线段在再最后分别多连两个点;

fi,j 表示维护一个连了 i 个点的线段,最后一个点为 j 时的方案数,有转移:

fi,j=k is connected to iknfi1,k

这东西可以前缀和维护,然后我们只需维护到 i=4 即可;

最后 C 直接算,对于 F 枚举最后一位的数 j,答案即为 j=1nf3,j×(dj)2,其中 dj 为能与 j 相连的点数;

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define int long long
const long long mod = 998244353;
int n, m;
long long f[5][100005], g[5][100005];
vector<int> v[100005];
main() {
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int x, y;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	for (int i = 1; i <= n; i++) {
		v[i].push_back(0);
		v[i].push_back(i);
		v[i].push_back(n + 1);
		sort(v[i].begin(), v[i].end());
		v[i].erase(unique(v[i].begin(), v[i].end()), v[i].end());
	}
	for (int i = 1; i <= n; i++) {
		f[1][i] = 1;
		g[1][i] = g[1][i - 1] + 1;
	}
	for (int i = 2; i <= 4; i++) {
		for (int j = 1; j <= n; j++) {
			for (int k = 1; k < v[j].size(); k++) {
				f[i][j] += (g[i - 1][v[j][k] - 1] - g[i - 1][v[j][k - 1]] + mod) % mod;
				f[i][j] = (f[i][j] + mod) % mod;
			}
		}
		for (int j = 1; j <= n; j++) {
			g[i][j] = g[i][j - 1] + f[i][j];
			g[i][j] = (g[i][j] + mod) % mod;
		}
	}
	long long ans = g[4][n] * g[4][n] % mod;
	long long sum = 0;
	for (int i = 1; i <= n; i++) {
		long long su = v[i].size() - 2;
		su = n - su;
		sum = (sum + f[3][i] * su % mod * su % mod + mod) % mod;
	}
	ans = ans * sum % mod;
	cout << ans;
	return 0;
}

铁路 60pts

赛时说不清的乱搞;

其实并不需要真的加点,只需要用并查集维护一下哪些点是被删掉的,然后把这个连通块连到这个新点即可;

好吧,其实原题解写的就这么简洁,所以我也不想写了

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
struct sss{
	int t, ne;
}e[2000005];
int h[2000005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
int f[500005][25];
int fa[1000005], dep[1000005];
int id[1000005];
int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void dfs(int x, int faa) {
	f[x][0] = faa;
	dep[x] = dep[faa] + 1;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (u == faa) continue;
		dfs(u, x);
	}
}
int ans;
int lca(int x, int y) {
	if (x == y) return x;
	if (dep[x] < dep[y]) swap(x, y);
	for (int i = 20; i >= 0; i--) {
		if (dep[f[x][i]] >= dep[y]) x = f[x][i];
	}
	if (x == y) return x;
	for (int i = 20; i >= 0; i--) {
		if (f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
void w(int x, int y, int now) {
	int sum = 0;
	x = id[x];
	y = id[y];
	int lc = find(lca(x, y));
	id[now] = lc;
	while(find(x) != lc) {
		fa[find(x)] = lc;
		x = find(f[x][0]);
		sum++;
	}
	while(find(y) != lc) {
		fa[find(y)] = lc;
		y = find(f[y][0]);
		sum++;
	}
	ans -= sum;
}
int main() {
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int x, y;
	for (int i = 1; i <= n - 1; i++) {
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n + m; i++) {
		fa[i] = i;
		id[i] = i;
	}
	dfs(1, 0);
	for (int j = 1; j <= 20; j++) {
		for (int i = 1; i <= n; i++) {
			f[i][j] = f[f[i][j - 1]][j - 1];
		}
	}
	ans = n;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		w(x, y, n + i);
		cout << ans << '\n';
	}
	return 0;
}

光纤 10pts

赛时直接输出 0 搞了10pts;

考虑正解,需要用到一个东西叫旋转卡壳

考虑最大的圆使其能够覆盖所有点,不难想到凸包(前提是你得学过),所以我们先求出凸包;

我们要考虑极限情况,即直线与圆相切的情况,那么我们最终的直线是要和至少一个圆相切的;

可以感性想一下,我们要求的这个直线是要把这个凸包“拦腰”截断的;

于是我们用旋转卡壳这个算法求出最长的点线距中的最小值,那么其一半的平方就是答案;

考虑证明一下,不难发现,把这条直线上下平移是更劣的,把其左右旋转会导致一边变大,一边变小;

最后,注意精度问题!,要用__int128long double 会丢精度!

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
#define int __int128
int n;
__int128 read() {
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
 
void out(__int128 x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) out(x / 10);
    putchar(x % 10 + '0');
}
struct poi{
	int x, y;
}p[1000005], s[1000005];
int cnt;
struct vec{
	int x, y;
	inline int operator ^(const vec &A) const {
		return x * A.y - y * A.x;
	}
};
inline vec operator -(const poi x, const poi y) {
	return vec{x.x - y.x, x.y - y.y};
}
struct line{
	poi p;
	vec v;
	line(poi x, vec y) {
		p = x;
		v = y;
	}
	line(poi x, poi y) {
		p = x;
		v = y - x;
	}
};
struct Q{
	__int128 a, b;
	long double val;
	bool operator >=(const Q &A) const {
		return val >= A.val;
	}
};
inline int slen(vec x) {
	return x.x * x.x + x.y * x.y;
}
inline bool cmp(poi x, poi y) {
	vec a = x - p[1];
	vec b = y - p[1];
	if ((a ^ b) > 0.0) return 1;
	else if ((a ^ b) == 0.0 && slen(a) <= slen(b)) return 1;
	else return 0;
}
vector<poi> G() {
	sort(p + 2, p + 1 + n, cmp);
	s[++cnt] = p[1];
	for (int i = 2; i <= n; i++) {
		vec b = p[i] - s[cnt];
		vec a = s[cnt] - s[cnt - 1];
		while(cnt > 1 && (a ^ b) <= 0.0) {
			cnt--;
			b = p[i] - s[cnt];
			a = s[cnt] - s[cnt - 1];
		}
		s[++cnt] = p[i];
	}
	s[++cnt] = p[1];
	vector<poi> a;
	a.clear();
	for (int i = 1; i <= cnt; i++) a.push_back(s[i]);
	return a;
}
__int128 aa, bb;
Q poi_to_line(poi x, line li) {
	return Q{(__int128)((li.v ^ (x - li.p)) * (li.v ^ (x - li.p))), slen(li.v), ((li.v ^ (x - li.p)) * ((li.v ^ (x - li.p)))) / slen(li.v)};
}
void R(vector<poi> a) {
	if (a.size() == 3) {
		cout << "0/1";
		exit(0);
	}
	int now = 0;
	double sum = 999999999999999999999.00;
	for (int i = 0; i < a.size() - 1; i++) {
		line li(a[i], a[i + 1]);
		while(poi_to_line(a[(now + 1) % a.size()], li) >= poi_to_line(a[now], li)) {
			now = (now + 1) % a.size();
		}
		Q c = poi_to_line(a[now], li);
		if (sum > c.val) {
			sum = c.val;
			aa = c.a;
			bb = c.b;
		}
	}
}
signed main() {
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	n = read();
	for (int i = 1; i <= n; i++) {
		int x, y;
		x = read();
		y = read();
		p[i].x = x;
		p[i].y = y;
		if (i != 1 && (p[i].y < p[1].y || (p[i].y == p[1].y && p[i].x < p[1].x))) swap(p[i], p[1]);
	}
	R(G());
	bb *= 4;
	__int128 gc = __gcd(aa, bb);
	aa /= gc;
	bb /= gc;
	out(aa);
	cout << '/';
	out(bb);
	return 0;
}
posted @   Peppa_Even_Pig  阅读(21)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示