1. 程式人生 > 其它 >題解 AtCoder Beginner Contest 168

題解 AtCoder Beginner Contest 168

小兔的話

歡迎大家在評論區留言哦~

AtCoder Beginner Contest 168


A - ∴ (Therefore)


B - ... (Triple Dots)


C - : (Colon)


D - .. (Double Dots)


E - ∙ (Bullet)

簡單題意

小兔捕獲了 \(N\) 條不同的沙丁魚,第 \(i\) 條沙丁魚的 美味程度香味程度 分別是 \(A_i\)\(B_i\)
她想在這些沙丁魚中選擇 一條 或者 多條 放入冷凍箱;但是必須保證沙丁魚的選擇是合格的
(合格的定義:其中的任意兩條沙丁魚 \(i\)\(j\) 都不滿足 \(A_i \times A_j + B_i \times B_j = 0\)

小兔想知道有多少種選擇沙丁魚的方法(選擇的沙丁魚的集合相同,算同一種方法),答案對 \(1e9 +7\) 取模

資料範圍

\(1 \leq N \leq 2 \times 10^5\)
\(-10^{18} \leq A_i, B_i \leq 10^{18}\)

知識點

  • 數學知識
    • 最大公約數 \(\mathrm{gcd}\)
    • 快速冪
  • STL
    • map
    • pair

分析

需要不滿足的式子與 \(i\)\(j\) 的關係太大了,不妨化簡一下:

\[A_i \times A_j + B_i \times B_j = 0 \to A_i \times A_j = - B_i * B_j \to \frac{A_i}{B_i} = - \frac{B_j}{A_j} \]

我們可以把 \(\frac{A_i}{B_i}\)

相同的分成一組,統計出屬於這一組的沙丁魚的數量,再把 \(\frac{A_i}{B_i}\)\(- \frac{B_j}{A_j}\) 的兩組分成一對,這一對肯定是互相滿足的(就是 \(C\)\(D\) 是一對,反過來 \(D\) 肯定與 \(C\) 是一對,\(D\) 不會和其它成為一對)
我們計算每一對裡的選擇方案,把所有的選擇方案數乘起來再 減一(排除全部不選的情況),就是最終的答案了

如何計算每一對裡的選擇方案呢?
可以先計算每一對中每組的選擇方案,設屬於這一組的沙丁魚有 \(s\) 條,選擇沙丁魚的方案數就是 \(2^s\)(每條魚有 被選擇不被選擇 \(2\) 種情況)
那麼每一對的方案數就是 \(s_1 + s_2 - 1\)

  • 因為其中的兩組是不能同時選的,所以是 \(+\) 而不是 \(\times\)
  • 因為在統計 \(s_1\) 被選的時候,\(s_2\) 一定是不選的;同理,在統計 \(s_2\) 被選的時候,\(s_1\) 一定是不選的;需要減去這 \(2\) 種多算的情況;又因為 \(2\) 組都不選也是 \(1\) 種合格的情況,所以又要加上 \(1\) 種情況,所以是 \(+1\)

程式碼

#include <cstdio>
#include <map>
#include <utility>
using namespace std;
#define int long long

int Gcd(int u, int v) { return (v == 0) ? u : Gcd(v, u % v); }
int Max(int u, int v) { return (u > v) ? u : v; }
int Min(int u, int v) { return (u < v) ? u : v; }

int rint()
{
    int x = 0, fx = 1; char c = getchar();
    while (c < '0' || c > '9') { fx ^= ((c == '-') ? 1 : 0); c = getchar(); }
    while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
    if (!fx) return -x;
    return x;
}

int qpow(int u, int v, int Mod)
{
	int ans = 1; u %= Mod;
	while (v)
	{
		if (v & 1) ans = ans * u % Mod;
		u = u * u % Mod; v >>= 1;
	}
	return ans;
}

const int MOD = 1e9 + 7;
const int MAX_n = 2e5;

int n, ans = 1, sum = 0;
int A[MAX_n + 5];
int B[MAX_n + 5];
map<pair<int, int>, int> G;
map<pair<int, int>, bool> vis;

signed main()
{
	n = rint();
	for (int i = 1; i <= n; i++)
	{
		A[i] = rint(), B[i] = rint();
		if (A[i] == 0 && B[i] == 0)
		{
			++sum; --i; --n; continue;
		}
		int temp = Gcd(A[i], B[i]);
		A[i] /= temp; B[i] /= temp;
		if (A[i] < 0) { A[i] = -A[i]; B[i] = -B[i]; }
		++G[make_pair(A[i], B[i])];
	}
	for (int i = 1; i <= n; i++)
	{
		pair<int, int> now = make_pair(A[i], B[i]);
		if (-B[i] < 0)  { B[i] = -B[i]; A[i] = -A[i]; }
		pair<int, int> other = make_pair(-B[i], A[i]);
		if (vis[now] || vis[other]) continue;
		vis[now] = vis[other] = true;
		ans = ans * ((qpow(2, G[now], MOD) + qpow(2, G[other], MOD) - 1) % MOD) % MOD;
	}
	printf("%lld\n", (ans - 1 + sum + MOD) % MOD);
	return 0;
}


F - . (Single Dot)