1. 程式人生 > 其它 >【2021夏紀中游記】2021.7.12模擬賽

【2021夏紀中游記】2021.7.12模擬賽

2021.7.12模擬賽

比賽概括:

\(\mathrm{sum}=40+0+0+5\)

唉。

T1 好元素:

題目大意:

找到 \(a_1+a_2+a_3=a_4\) 的個數(\(1,2,3\) 可以相等)。

思路:

一眼題,用將 \(a_1+a_2+a_3=a_4\) 轉移為 \(a_1+a_2=a_4-a_3\),然後 \(\mathcal{O}(n^2)\) 加雜湊。

但是不能用 map,否則回超時。

程式碼:

const int N = 5e3 + 10;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, mod = 25000003, ans;
ll a[N];
int hash[25000005];

inline int Hash(int val, int op = 1)
{
	int x = (val % mod + mod) % mod;
	for (; hash[x] != 1010580540 && hash[x] != val; x = (x + 1) % mod);
	if (op)
		return x;
	hash[x] = val;
	return 0;
}

int main()
{
	freopen("good.in", "r", stdin);
	freopen("good.out", "w", stdout);
	memset (hash, 60, sizeof hash);
	n = Read();
	for (register int i = 1; i <= n; i++)
	{
		a[i] = Read();
		for (register int j = 1; j < i; j++)
			if(hash[Hash(a[i] - a[j])] == a[i] - a[j]) {ans++; break;}
		for (register int j = 1; j <= i; j++)
			if(hash[Hash(a[i] + a[j])] != a[i] + a[j]) Hash(a[i] + a[j], 0);
	}
	printf ("%d\n", ans);
	return 0;
}

T2 最短路徑:

題目大意:

在平面直角座標系內的 \(n\) 個點,選擇兩條路徑使得所有點必須被經過,且 \(b1\) 必在路線一、\(b2\) 必在路線二。

思路:

由於確定了路線一,路線二就也確定了,所以一開始想到的狀態是設 \(f_{i,j}\) 表示路線一從 \(j\) 來到 \(i\) 的最小值,但是這麼設會導致路線二很難連線。

因此,考慮兩個路線一起設,設 \(f_{i,j}\) 表示路線一正著走到 \(i\)、路線二倒著走到 \(j\) 的最小數。則有:

\[\begin{aligned} f_{i,k}&=\min\{f_{i,j}+\mathrm{dis}(j,k)\}\\ f_{k,j}&=\min\{f_{i,j}+\mathrm{dis}(i,k)\} \end{aligned}\]

其中 \(k=\max(i,j)+1\)

\(n\) 時要特判處理。

程式碼:

const int N = 1010;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, b1, b2;
struct point
{
	int x, y, id;
}a[N];

bool cmp (point a, point b)
{
	return a.x < b.x;
}

double f[N][N];

double Dis(int i, int j)
{
	return sqrt((a[i].x - a[j].x) * (a[i].x - a[j].x) +
	 (a[i].y - a[j].y) * (a[i].y - a[j].y));
}

int main()
{
	freopen("path.in", "r", stdin);
	freopen("path.out", "w", stdout);
	n = Read(), b1 = Read(), b2 = Read(); b1++, b2 ++;
	for (int i = 1; i <= n; i++)
		a[i].x = Read(), a[i].y = Read(), a[i].id = i;
	sort (a + 1, a + 1 + n, cmp);
	a[0] = a[1];
	for (int i = 1; i <= n; i++) 
	{
		if (a[i].id == b1) b1 = a[i].id;
		if (a[i].id == b2) b2 = a[i].id;
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			f[i][j] = 1e9;
	f[1][1] = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
		{
			int k = max(i, j) + 1;
			if (max(i, j) == n) 
			{
				if (i == n) f[n][n] = min(f[n][n], f[n][j] + Dis(j, n));
				else f[n][n] = min(f[n][n], f[i][n] + Dis(i, n));
				continue;
			}
			if (k != b1) f[i][k] = min(f[i][k], f[i][j] + Dis(j, k));
			if (k != b2) f[k][j] = min(f[k][j], f[i][j] + Dis(i, k));
		}
	printf ("%.2lf\n", f[n][n]);
	return 0;
}

T3 [GDOI2016]最長公共子串:

題目大意:

給兩個字串 \(s,t\)。可對 \(s\) 的一些區間進行任意排列,使得 \(s,t\) 的最長公共子串最大,求其長度。

正文:

難確實難。有一個很顯然的性質,有交集的區間就讓它們並在一起,使範圍縮小到 \(O(n)\) 級。

然後考慮匹配,由於區間內的可以按任意順序,我們就把區間看作一個整體,匹配就是和 \(t\) 比字母數量。設 \(f_{i,j}\) 表示 \(s\)\(i\) 區間、\(t\)\(j\) 字元開始的最長長度。那麼就照上文說的,比字母數量。

得到了 \(f\) 後,就能得到 \(g_{i,j}\) 表示 \(s\)\(i\) 區間開始、\(t\)\(j\) 字元開始的最長長度。

然後隨便統計答案。

總結:本題思路以小見大(\(f\rightarrow g\))。

程式碼:

const int N = 2010, M = 1e5 + 10;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

char s[N], t[N];
int n, m, k;
struct node
{
	int l, r;
	bool operator < (const node &a) const
	{
		return l < a.l;
	}
}a[M];
int num[N][27];
int f[N][N], g[N][N];
int bucket[27];
int tot, ans;

int main()
{
	freopen("lcs.in", "r", stdin);
	freopen("lcs.out", "w", stdout);
	scanf("%s%s", t + 1, s + 1);
	n = strlen(t + 1), m = strlen(s + 1);
	k = Read();
	for (int i = 1; i <= k; i++)
		a[i].l = Read() + 1, a[i].r = Read() + 1;
	for (int i = 1; i <= n; i++)
		a[++k] = (node) {i, i};
	sort (a + 1, a + 1 + k);
	tot = 1;
	for (int i = 2; i <= k; i++)
	{
		if (a[i].l <= a[tot].r) a[tot].r = max(a[tot].r, a[i].r);
		else a[++tot] = a[i];
	}
	k = tot;
	for (int i = 1; i <= k; i++)
		for (int j = a[i].l; j <= a[i].r; j++)
			num[i][s[j] - 'a']++;
	
	for (int i = k; i; i--)
		for (int j = m; j; j--)
		{
			int len = a[i].r - a[i].l + 1, l = j;
			for (; l <= min(m, len + j - 1); l++)
			{
				if (num[i][t[l] - 'a'] == bucket[t[l] - 'a']) break;
				bucket[t[l] - 'a'] ++;
			}
			f[i][j] = l - j;
			if (f[i + 1][l] == a[i + 1].r - a[i + 1].l + 1)
				g[i][j] = f[i][j] + g[i + 1][l];
			else g[i][j] = f[i][j] + f[i + 1][l];
			for (int p = j; p <= l; p++)  // [j,l] 是當前區間絕對可以匹配的,找下一個區間最優匹配 
			{
				if (p != l) bucket[t[p] - 'a'] --;
				if (f[i + 1][p] == a[i + 1].r - a[i + 1].l + 1)
					ans = max(ans, p - j + g[i + 1][p]);
				else ans = max(ans, p - j + f[i + 1][p]);
			}
		}
	printf ("%d\n", ans);
	return 0;
}

T4 Vani和Cl2捉迷藏:

題目大意:

最大獨立集板題。

正文:

板題不講。

程式碼:

const int N = 210, M = 30010;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, k;

bool G[N][N];

int con[N];
bool vis[N];

bool Hungary(int u)
{
	for (int v = 1; v <= n; v++)
	{
		if (!G[u][v]) continue;
		if (vis[v]) continue;
		vis[v] = 1;
		if (!con[v] || Hungary(con[v]))
		{
			con[v] = u;
			return 1;
		}
	}
	return 0;
}

int main()
{
//	freopen("travel10.in", "r", stdin);
//	freopen(".out", "w", stdout);
	n = Read(), k = Read();
	for (int i = 1, u, v; i <= k; i++)
		u = Read(), v = Read(), G[u][v] = 1;
		
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			if (i != k)
				for (int j = 1; j <= n; j++)
					if (j != i && j != k)
						G[i][j] |= G[i][k] & G[k][j];
	int ans = 0;
	for (int i = 1; i <= n; i++)
	{
		memset(vis, 0, sizeof vis);
		if(Hungary(i)) ans++;
	}
	printf ("%d\n", n - ans);
    return 0;
}