//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.7.29解题报告

2023.7.29解题报告

T1

我们把每一个一开始给定的危险的数标记一下,然后用埃氏筛的原理,把一开始危险的数的倍数都标记为危险的,然后统计一下就好了。

#include <bits/stdc++.h>

#define int long long
#define N 20000100
#define M 100010

using namespace std;

int n, m, a[N], b[M], ans;

signed main()
{
//	freopen("a4.in", "r", stdin);
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= m; i ++)
		scanf("%lld", &b[i]);
	for(int i = 1; i <= m; i ++)
	{
		int xx = 1;
		while(xx * b[i] <= n) a[xx * b[i]] = 1, xx ++;
	}
	for(int i = 1; i <= n; i ++)
		if(a[i]) ans ++;
	cout << ans << endl;
	return 0;
}

T2

考试打的暴力。

直接把从 \(2\)\(n\) 的所有父节点处理出来连边,然后倍增 LCA。

#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

int n, m, head[N], cnt, dep[N], f[N][21];
struct sb{int u, v, next;}e[N << 1];

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

inline void add(int u, int v)
{
	e[++ cnt] = (sb){u, v, head[u]};
	head[u] = cnt;
}

inline int pd(int x)
{
	if(x == 2) return 1;
	for(int i = 2; i <= sqrt(x); i ++)
		if(x % i == 0) return 0;
	return 1;
}

inline int fid(int x)
{
	for(int i = x; i >= 2; i --)
		if(x % i == 0)
			if(pd(i)) return (x / i);
}

inline void dfs(int x, int fa)
{
	dep[x] = dep[fa] + 1;
	f[x][0] = fa;
	for(int i = 1; i <= 20; i ++)
		f[x][i] = f[f[x][i - 1]][i - 1];
	for(int i = head[x]; i; i = e[i].next)
	{
		int v = e[i].v;
		if(v == fa) continue;
		dfs(v, x);
	}
}

inline int LCA(int x, int y)
{
	if(dep[x] < dep[y]) swap(x, y);
	int res = 0;
	for(int i = 20; i >= 0; i --)
		if(dep[f[x][i]] >= dep[y]) x = f[x][i], res += pow(2, i);
	if(x == y) return res;
	for(int i = 20; i >= 0; i --)
	{
		if(f[x][i] != f[y][i])
		{
			x = f[x][i];
			y = f[y][i];
			res += pow(2, i) * 2;
		}
	}
	return res + 2;
}

signed main()
{
//	freopen("b3.in", "r", stdin);
//	freopen("55.out", "w", stdout);
	n = read(), m = read();
//	cout << "n : " << n << endl;
	for(int i = 2; i <= n; i ++)
	{
//		cout << "cao: " << i << endl;
		int j = fid(i);
//		cout << "JJJ :::   " << j << endl;
		add(i, j);
		add(j, i);
	}
	dfs(1, 0);
	for(int i = 1; i <= m; i ++)
	{
		int u = read(), v = read();
		int ans = LCA(u, v);
		cout << ans << endl;
	}
	return 0;
}

我们利用线性筛的原理,我们知道一个质数的最大质因子就是他本身,那么我们就可以直接将质数的 \(f(i)\) 给存起来,然后对于合数的情况,我们发现是被 \(pm[j]\times i\) 筛去的,那么我们就知道,当前合数的 \(f(i)=\max(f[i], pm[j])\)

然后我们直接暴力跳求 LCA,打个表可以发现深度最大的不会超过 \(24\),所以暴力跳就很快,用倍增在预处理的时候反而会 \(\text{TLE}\)

#include <bits/stdc++.h>

#define int long long
#define N 10001000
#define endl '\n'

using namespace std;

int n, m, f[N], cnt, pm[N], dep[N];

bitset<N> vis;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

inline void init()
{
	for(int i = 2; i <= n; i ++)
	{
		if(!vis[i]) pm[++ cnt] = i, f[i] = i, dep[i] = 2;
		for(int j = 1; j <= cnt && pm[j] * i <= n; j ++)
		{
			int tmp = pm[j] * i;
			vis[tmp] = 1;
			f[tmp] = max(f[i], pm[j]);
			dep[tmp] = dep[tmp / f[tmp]] + 1;
			if(i % pm[j] == 0) break;
		}
	}
	return ;
}

signed main()
{
	n = read(), m = read();
	dep[1] = f[1] = 1;
	init();
	while(m --)
	{
		int u = read(), v = read(), ans = 0;
		ans = dep[u] + dep[v];
		if(dep[u] < dep[v]) swap(u, v);
		while(dep[u] != dep[v]) u = u / f[u];
		while(u != v) u = u / f[u], v = v / f[v];
		cout << ans - 2 * dep[u] << endl;
	}
	return 0;
}

T3

暴力,直接枚举 \(x,y\) 然后暴力判断,累加答案。

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int n, ans;

inline int fid(int x)
{
	int res = 0;
	for(int i = 1; i < x; i ++)
		if((x + i) % (x - i) ==0) res ++;
	return res;
}

signed main()
{
	cin >> n;
	for(int i = 2; i <= n; i ++)
	{
		int res = fid(i);
//		cout << res << endl;
		ans += res;
	}
	cout << ans << endl;
	return 0;
}

我们要求的是:

\[\sum_{y = 2}^{n}\sum_{x=1}^{y - 1} [(y-x)|(y + x)] \]

我们设 \(t = y - x\),那么就是:

\[\text{原式}= \sum_{y=2}^{n}\sum_{t = 1}^{y - 1}[t|(2y-t)]=\sum_{t = 1}^{n - 1} \sum_{y = t + 1}^{n}[t|(2y-t)] \]

然后我们发现后面的一个循环实际上是可以直接算的,要分两种情况讨论。

\(t\) 是偶数,那么我们发现里面的都是偶数的情况,答案就是 \(\frac{2n-t}{t} - 1\),减一是因为前面的 \([1,t + 1]\) 里面有一个 \(t\) 的倍数。

\(t\) 是奇数,那么我们发现除了上面的,还需要减去偶数的倍数的情况,因为当倍数是偶数的时候是不合法的。

那么我们就直接减去 \(\frac{2n-t}{2t}\)

#include <bits/stdc++.h>

#define int long long

using namespace std;

int n, ans;

signed main()
{
	cin >> n;
	for(int t = 1; t < n; t ++)
	{
		ans += (n + n - t) / t - 1;
		if(t & 1) ans -= (n + n - t) / (2 * t);
	}
	cout << ans << endl; 
	return 0;
}

T4

暴力。

我们直接枚举 \(k\) 个位置填 \([1,n]\) 的数的情况,然后直接暴力计算最小公倍数用 \(map\) 存下来,然后直接遍历一遍 \(n\) 然后输出就好了。

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int n, k, a[N], mp[N];

inline int LCM()
{
	int last = a[1];
	for(int i = 2; i <= k; i ++)
	{
		int gcd = __gcd(last, a[i]);
		last = last * a[i] / gcd;
	}
	return last;
}

inline void dfs(int x)
{
	if(x == k + 1)
	{
		int lcm = LCM();
		mp[lcm] ++;
		return ;
	}
	for(int i = 1; i <= n; i ++)
	{
		a[x] = i;
		dfs(x + 1);
	}
	return ;
}

signed main()
{
	cin >> n >> k;
	dfs(1);
	for(int i = 1; i <= n; i ++)
		cout << mp[i] << " ";
	return 0;
}= 1; i <= n; i ++)
		cout << mp[i] << " ";
	return 0;
}

首先,如果一个长为 \(k\) 的序列的最小公倍数是 \(i\),那么这个序列的所有元素都是 \(i\) 的约数。设

的约数有 \(d(i)\) 个。

\(k\)\(i\) 的约数的方案是 \(g_{i}=d(i)^{k}\)。但如果要求最小公倍数是 \(i\),则这样任意填可能导致最小

公倍数不是 \(i\)

设最小公倍数是 \(i\) 的序列个数是 \(f_{i}\),那么可以先求出 \(g_{i}\)(任意填 \(i\) 的约数),再减去最终最小公

倍数不是 \(i\) 的方案数,那么最小公倍数不是 \(i\),就一定是 \(i\) 的某一个约数 \(j\),且最小公倍数是

\(j\) 的数组所有元素一定是 \(i\) 的约数,也就是它一定会在 \(g_{i}\) 中被算进去。

所以 \(f_{i} = g_{i}-\sum_{j|i,j\ne i}^{}f_{j}\)

这样暴力递推的复杂度是 \(O(n \log n)\) 的,总时间复杂度 \(O(n \log n + n \log k)\)

#include <bits/stdc++.h>

#define int long long
#define P 998244353
#define N 10001000

using namespace std;

int n, k, d[N], f[N], g[N];

int ksm(int x, int y)
{
	int res = 1;
	while (y)
	{
		if (y & 1) res = x * res % P;
		x = x * x % P, y >>= 1;
	}
	return res;
}

signed main()
{
	cin >> n >> k;
	for(int i = 1; i <= n; i ++)
		for(int j = i; j <= n; j += i)
			d[j] ++;
	for(int i = 1; i <= n; i ++)
		f[i] = g[i] = ksm(d[i], k);
	for(int i = 1; i <= n; i ++)
		for(int j = 2 * i; j <= n; j += i)
			f[j] = ((f[j] - f[i]) % P + P) % P;
	for(int i = 1; i <= n; i ++)
		cout << f[i] << " ";
	return 0;
}

.

posted @ 2023-07-29 22:29  北烛青澜  阅读(6)  评论(0)    收藏  举报