• 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;
    }
    
  • 相关阅读:
    Python-内置函数
    Python-匿名函数
    Python-函数递归-二分法
    Python-函数递归
    Day4-函数
    CSS-定位
    CSS-文本属性,文字属性
    CSS-浮动
    CSS-盒模型
    CSS-继承和层叠
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12232253.html
Copyright © 2020-2023  润新知