C. Pull Your Luck
https://codeforces.com/contest/1804/problem/C
题意:是否存在一个 a ≤ p a\leq p a≤p使得 ( x + a ∗ ( a + 1 ) 2 ) % n = 0 (x+\frac{a*(a+1)}{2} )\%n=0 (x+2a∗(a+1))%n=0
n ( 1 ≤ ∑ n ≤ 2 ⋅ 1 0 5 ) , p ( 1 ≤ p ≤ 1 0 9 ) n(1\leq \sum n\leq 2\cdot10^5),p(1\leq p\leq 10^9) n(1≤∑n≤2⋅105),p(1≤p≤109)
思路:枚举a。容易发现对
a
∗
(
a
+
1
)
2
%
n
\frac{a*(a+1)}{2} \%n
2a∗(a+1)%n是有周期性的。
结论: 当n为奇数时最小周期为n,n为偶数时为2n
证明:
a ∗ ( a + 1 ) 2 % n ⇔ ( a % n ) ∗ ( ( a + 1 ) % n ) ∗ i n v ( 2 ) \frac{a*(a+1)}{2}\%n \Leftrightarrow (a\%n)*((a+1)\%n)*inv(2)%n 2a∗(a+1)%n⇔(a%n)∗((a+1)%n)∗inv(2)
i n v ( 2 ) inv(2) inv(2)为2在模n下的逆元,当逆元存在的时候,很明显周期T=n。
-
当n为奇数: i n v ( 2 ) = n + 1 2 inv(2)=\frac{n+1}{2} inv(2)=2n+1,因为 ( 2 ∗ n + 1 2 ) % n = 1 (2*\frac{n+1}{2})\%n=1 (2∗2n+1)%n=1
-
当n为偶数时,逆元不存在:定义a存在逆元,存在一个x使得 a x ≡ 1 ax\equiv 1 ax≡1,也即 a x + n y = 1 ax+ny=1 ax+ny=1存在正整数解x。
而由扩展欧几里得可知, a x + n y = 1 ax+ny=1 ax+ny=1有解的必要条件是 1 = g c d ( a , n ) 1=gcd(a,n) 1=gcd(a,n),而 g c d ( a , n ) ≥ 2 ≠ 1 gcd(a,n)\ge2 \ne1 gcd(a,n)≥2=1故逆元不存在。
在逆元不存在的情况下,上式就不成立了。
我们令周期为kn(容易知道周期肯定是n的倍数),有
a ∗ ( a + 1 ) 2 % n = ( a + k n ) ∗ ( a + k n + 1 ) 2 % n \frac{a*(a+1)}{2}\%n=\frac{(a+kn)*(a+kn+1)}{2}\%n 2a∗(a+1)%n=2(a+kn)∗(a+kn+1)%n
= a 2 + a + 2 k n a + k n + k 2 n 2 2 =\frac{a^2+a+2kna+kn+k^2n^2}{2} =2a2+a+2kna+kn+k2n2
= a 2 + a 2 % n + k n + k 2 n 2 2 % n =\frac{a^2+a}{2}\%n+\frac{kn+k^2n^2}{2}\%n =2a2+a%n+2kn+k2n2%n
= a ∗ ( a + 1 ) 2 % n + k n + k 2 n 2 2 % n =\frac{a*(a+1)}{2}\%n+\frac{kn+k^2n^2}{2}\%n =2a∗(a+1)%n+2kn+k2n2%n
那么,我们就需要后面一项 k n + k 2 n 2 2 % n = 0 \frac{kn+k^2n^2}{2}\%n=0 2kn+k2n2%n=0,很明显k=2时该式子为0。
AC代码
int calc(int x) {
return x * (x + 1) / 2;
}
void solve() {
cin >> n >> x >> p;
for (int i = 1; i <= min((n & 1) ? n : n << 1, p); i++) {
if ((calc(i) + x) % n == 0) {
cout << yes;
return;
}
}
cout << no;
}
D. Accommodation (贪心)
https://codeforces.com/contest/1804/problem/D
题意:有n层公寓,每层m个窗户(m是4的倍数)。每个窗户要么是明亮的,要么是昏暗的。
每间房间要么是一居室,房间内有一个窗户,要么是两居室,房间内是同一层的连续两个窗户。每一层中,恰好有 m 2 \frac{m}{2} 2m个一居室和 m 4 \frac{m}{4} 4m 个两居室。
对于一间公寓,只要其中有一个窗户是明亮的,则称这间公寓是明亮的。对于所有划分公寓的方法,求明亮的公寓的数量的最大值和最小值。
思路:对每层贪心考虑
最小值:那肯定是尽可能将两个亮的窗户划分成一个大房间(记为c11)
- c 11 ≤ m 4 c11\le\frac{m}{4} c11≤4m:两居室还有剩余,在这种情况下剩下的1都能够点亮1个房间,答案为 c 11 + c 1 − c 11 ∗ 2 c11+c1-c11*2 c11+c1−c11∗2(c1为1的数量)
- c 11 > m 4 c11>\frac{m}{4} c11>4m:11能够填满所有的两居室,那么剩下的也就都是一居室了,答案为 m 4 + ( c 1 − m 4 ∗ 2 ) \frac{m}{4}+(c1-\frac{m}{4}*2) 4m+(c1−4m∗2)
最大值:尽可能不让11划分成两居室,记c00为能划分成非11的最多两居室的数量
- c 00 ≤ m 4 c00\le\frac{m}{4} c00≤4m:两居室还有剩余,则必须要用11作为两居室划分成剩余的两居室,其余的每个1都能够点亮一个房间,此时答案为 c 1 − ( m 4 − c 00 ) c1-(\frac{m}{4}-c00) c1−(4m−c00)
- c 00 > m 4 c00>\frac{m}{4} c00>4m:显然所有的1都能够点亮1个房间,答案为 c 1 c1 c1
详见代码
void solve() {
cin >> n >> m;
char a[n + 1][m + 1];
vector<int> c0(n + 1, 0), c1(n + 1, 0), c00(n + 1, 0), c11(n + 1, 0);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j], c1[i] += (a[i][j] == '1');
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j < m; j++) {
if (!(a[i][j] == '1' && a[i][j + 1] == '1'))//计算不把11划分成两居室的数量
c00[i]++, j++;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < m; j++) {
if (a[i][j] == '1' && a[i][j + 1] == '1')//计算11的数量
c11[i]++, j++;
}
}
for (int i = 1; i <= n; i++) {
int last = c1[i] - 2 * c11[i];
if (c11[i] <= m / 4) ans1 += c11[i] + last;
else ans1 += m / 4 + (c1[i] - m / 2);
if (c00[i] <= m / 4) ans2 += c1[i] - (m / 4 - c00[i]);
else ans2 += c1[i];
}
cout << ans1 << " " << ans2 << "\n";
}