FJWC2020 Day2 题解

T1

Description

给定长度为 \(n\) 的数组 \(h\),现在你有一个全 \(0\) 数组 \(a\),每次你可以执行下面两个操作之一:

  1. \(a_i++,a_{i+1}+=2,i∈[1,n-1]\)
  2. \(a_i+=2,a_{i+1}++,i∈[1,n-1]\)

求最少的操作次数使得 \(\forall 1\leq i\leq n,a_i\geq h_i\)

\(n\leq 10^6\)

\(3\) 题时空限制均为 \(1s/512MB\)

Solution

先考虑一个显然错误的贪心。遍历一遍,当 \(a_i<h_i\) 时,就令 \(a_i+=2,a_{i+1}++\)。否则 \(i++\)。这样只能保证前面的 \(i\) 的操作次数最少,不能保证总操作次数最少。

因此考虑如下的反悔操作:

  1. 若之前执行了 \(a_i+=2,a_{i+1}++\)。考虑撤销这个操作,改成 \(a_i++,a_{i+1}+=2\),然后再加一步 \(a_i++,a_{i+1}+=2\)。这样使得 \(a_i\) 不变,并且花 \(1\) 的代价让 \(a_{i+1}+=3\)
  2. 若之前执行了 \(a_i+=3\)。考虑撤销这个操作,改成 \(a_i++,a_{i+1}+=2\),然后再加一步 \(a_i+=2,a_{i+1}++\)。这样使得 \(a_i\) 不变,并且花 \(1\) 的代价让 \(a_{i+1}+=3\)
  3. 若之前执行了 \(a_i+=3\)。考虑撤销这个操作,但是加上 \(3\) 次的 \(a_i++,a_{i+1}+=2\)。这样使得 \(a_i\) 不变,并且花 \(2\) 的代价让 \(a_{i+1}+=6\)

\(3\) 种反悔操作是足够的。因为反悔必须保证 \(a_i\) 不变。因此操作 \(a_i++,a_{i+1}+=2\) 无法撤销。撤销 \(a_i+=2,a_{i+1}++\) 之后只能加上两个含 \(a_i++\) 的操作才能保证 \(a_i\) 不变。撤销 \(a_i+=3\) 之后只能加上一个含 \(a_i++\),一个含 \(a_i+=2\) 的操作,或者加上 \(3\) 个含 \(a_i++\) 的操作,才能保证 \(a_i\) 不变。

我们的贪心还是保证靠前的 \(a_i\) 的操作次数尽量小。因此记一下之前用了几个 \(a_{i-1}+=3\),用了几个 \(a_{i-1}+=2,a_i++\),即可知道能用多少次 \(a_i+=3\)。如果次数都用完了还是 \(a_i<h_i\),设此时 \(h_i-a_i=k\),那么我们用 \(\lfloor\frac{k}{2}\rfloor\)\(a_i+=2,a_{i+1}++\),用 \(k\%2\)\(a_i++,a_{i+1}+=2\) 即可。

时间复杂度 \(O(n)\)

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

const int e = 1e6 + 5;

int t1, t2, t3, h[e], a[e], n;
ll ans;

int main()
{
	read(n);
	int i;
	for (i = 1; i <= n; i++) read(h[i]);
	for (i = 1; i <= n; i++)
	{
		h[i] = max(0, h[i]);
		t1 = min(h[i] / 3, a[i]);
		h[i] -= t1 * 3;
		t2 = h[i] / 2;
		h[i] -= t2 * 2;
		t3 = h[i];
		h[i] -= t3;
		ans += t1 + t2 + t3;
		a[i + 1] += t1 * 2 + t2;
		h[i + 1] -= t2 + t3 * 2; 
	}
	cout << ans << endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2

Description

给你一个含 \(n\) 个珠子的环。你要对这个环染色。每个珠子可以染上 \(k\) 种颜色之一或者不染,不能有相邻两个珠子同时染色。

如果一种方案旋转之后可以变成另一种,那么这两种方案本质相同。求本质不同的染色方案数 \(\%10^9+7\)

\(n,k\leq 10^9\)

Solution

菜鸡流下了不会 \(\text{Burnside}\) 的泪水。

\(\text{Burnside}\) 引理:$$ans=\frac{1}{|G|}\sum_{g∈G}f(g)$$ 即本质不同的方案数为各个置换下不动方案数的平均数。

本题的置换集合为:旋转 \(i\) 个位置,\(\forall 1\leq i\leq n\)

现在我们要求旋转 \(i\) 个位置下的不动方案数。令 \(t=gcd(n,i)\),显然合法的方案中,可以把 \(n\) 个珠子分成 \(t\) 组,每组 \(\frac{n}{t}\) 个。同组珠子必须同色,相邻组的珠子不能同时染色,包括第 \(1\) 组和第 \(t\) 组。

问题转化为对一个长度为 \(t\) 的环进行染色,每个点可以染 \(k\) 种颜色种的一种,或者无色,相邻的点至少一个无色,求方案数。

考虑 \(\text{dp}\),令 \(f[i][j][k]\) 表示前 \(i\) 个点,\(1\) 号点是否染色,\(i\) 号点是否染色的方案数。可以用矩阵乘法优化到 \(O(\log t)\)

发现 \(gcd(n,i)\) 相同的 \(i\) 的答案是一样的。那么枚举 \(n\) 的约数 \(t\),易得:$$ans=\frac{1}{n}\sum_{t|n}calc(t)×\phi(\frac{n}{t})$$

其中 \(\phi(\frac{n}{t})\) 表示 \(gcd(n,i)=t\)\(i\) 的个数。

时间复杂度 \(O(\sqrt n\log n)\)

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int N = 1e6, mod = 1e9 + 7, e = 1e6 + 5;

int n, m, ans, pri[e], cnt;
bool bo[e];

struct matrix
{
	int r, c, a[2][2];
	
	matrix(){}
	matrix(int _r, int _c) :
		r(_r), c(_c) {}
};

inline void add(int &x, int y)
{
	(x += y) >= mod && (x -= mod); 
}

inline int mul(int x, int y)
{
	return (ll)x * y % mod;
}

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

inline matrix operator * (matrix a, matrix b)
{
	matrix res = matrix(a.r, b.c);
	memset(res.a, 0, sizeof(res.a));
	int i, j, k;
	for (i = 0; i < a.r; i++)
	for (k = 0; k < a.c; k++)
	for (j = 0; j < b.c; j++)
	res.a[i][j] = (res.a[i][j] + (ll)a.a[i][k] * b.a[k][j]) % mod;
	return res;
}

inline matrix operator ^ (matrix a, int y)
{
	matrix res = matrix(a.r, a.c);
	memset(res.a, 0, sizeof(res.a));
	res.a[0][0] = res.a[1][1] = 1;
	while (y)
	{
		if (y & 1) res = res * a;
		y >>= 1;
		a = a * a;
	}
	return res;
}

inline int calc(int t)
{
	matrix c = matrix(1, 2), b = matrix(2, 2), a;
	int res = 0;
	c.a[0][0] = 1; c.a[0][1] = 0;
	b.a[0][0] = b.a[1][0] = 1; b.a[0][1] = m; b.a[1][1] = 0;
	a = c * (b ^ (t - 1));
	add(res, a.a[0][0]); add(res, a.a[0][1]);
	
	c.a[0][0] = 0; c.a[0][1] = m;
	b.a[0][0] = b.a[1][0] = 1; b.a[0][1] = m; b.a[1][1] = 0;
	a = c * (b ^ (t - 1));
	add(res, a.a[0][0]);
	return res;
}

inline void init()
{
	int i, j;
	for (i = 2; i <= N; i++)
	{
		if (!bo[i]) pri[++cnt] = i;
		for (j = 1; j <= cnt && i * pri[j] <= N; j++)
		{
			bo[i * pri[j]] = 1;
			if (i % pri[j] == 0) break;
		}
	}
}

inline int phi(int x)
{
	int res = x;
	for (int i = 1; i <= cnt && x >= pri[i]; i++)
	if (x % pri[i] == 0)
	{
		res = (ll)res * (pri[i] - 1) / pri[i];
		while (x % pri[i] == 0) x /= pri[i];
	}
	if (x != 1) res = (ll)res * (x - 1) / x;
	return res;
}

int main()
{
	init();
	cin >> n >> m;
	int s = sqrt(n), i;
	for (i = 1; i <= s; i++)
	if (n % i == 0)
	{
		add(ans, mul(phi(i), calc(n / i)));
		if (n / i != i) add(ans, mul(phi(n / i), calc(i)));
	}
	ans = mul(ans, ksm(n, mod - 2));
	cout << ans << endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

Description

给定两个数 \(n,k\),接下来有 \(n\) 次操作,每次可能是加入一个数 \(x\),或删除一个数 \(x\)。每次操作之后要回答有几对数的 \(gcd=k\)

\(1\leq n,k,x\leq 10^5\)

Solution

把每个数 \(÷\ k\),忽略 \(\%\ k≠0\) 的数。问题转化为求 \(gcd=1\) 的数的对数。

\(cnt_i\) 表示 \(i\) 的倍数有几个,则答案为 $$\sum_{i=1}{105}\mu_i×C_{cnt_i}^2$$ 所以加入一个数 \(x\) 的时候,只要枚举 \(x\) 的约数 \(y\),把 \(cnt_y++\),同时计算新的答案即可。删除同理。

时间复杂度 \(O(n\sqrt x)\)\(10^5\) 以内的数的约数最多 \(128\) 个。

Code

#include <bits/stdc++.h>

using namespace std;

#define ll long long

template <class t>
inline void read(t & res)
{
	char ch;
	while (ch = getchar(), !isdigit(ch));
	res = ch ^ 48;
	while (ch = getchar(), isdigit(ch))
	res = res * 10 + (ch ^ 48);
}

template <class t>
inline void print(t x)
{
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

const int e = 2e5 + 5;

vector<int>g[e];
int n, k, cnt[e], a[e], m = 100000, miu[e];
ll ans;
bool bo[e];

inline ll c2(ll x)
{
	return x * (x - 1) / 2;
}

inline void add(int x)
{
	if (x % k) return;
	a[x]++;
	x /= k;
	int len = g[x].size(), i;
	for (i = 0; i < len; i++)
	{
		int y = g[x][i];
		ans -= miu[y] * c2(cnt[y]);
		cnt[y]++;
		ans += miu[y] * c2(cnt[y]);
	}
}

inline void del(int x)
{
	if (x % k) return;
	if (!a[x]) return;
	a[x]--;
	x /= k;
	int len = g[x].size(), i;
	for (i = 0; i < len; i++)
	{
		int y = g[x][i];
		ans -= miu[y] * c2(cnt[y]);
		cnt[y]--;
		ans += miu[y] * c2(cnt[y]);
	}
}

int main()
{
	int op, x, i, j;
	for (i = 1; i <= m; i++)
	for (j = i; j <= m; j += i)
	g[j].push_back(i);
	for (i = 1; i <= m; i++) miu[i] = 1;
	for (i = 2; i <= m; i++)
	if (!bo[i])
	{
		miu[i] = -1;
		for (j = 2 * i; j <= m; j += i)
		{
			bo[j] = 1;
			if (j / i % i == 0) miu[j] = 0;
			else miu[j] *= -1;
		}
	}
	read(n); read(k);
	while (n--)
	{
		read(op); read(x);
		if (op == 0) del(x);
		else add(x);
		print(ans); putchar('\n');
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2020-01-24 15:32  花淇淋  阅读(693)  评论(1编辑  收藏  举报