1. P10447 最短 Hamilton 路径
著名 NPC 问题。
发现 n ≤ 20 n \le 20 n≤20,考虑状压 dp。那么设 f i , j f_{i,j} fi,j 表示当前在第 i i i 个点, j j j 是一个 n n n 位二进制数, j j j 在二进制下第 i i i 位表示第 i i i 个点是否已经到达。那么对于这个状态,我们直接找下一个该去哪个点。也就是,假设枚举到下一个去第 k k k 个点,那么有转移方程
f k , j + 2 n − k = min ( f k , j + 2 n − k , f i , j + d i s i , k ) f_{k,j+2^{n-k}}=\min(f_{k,j+2^{n-k}},f_{i,j}+dis_{i,k}) fk,j+2n−k=min(fk,j+2n−k,fi,j+disi,k)
那么最终答案就是 f n , 2 n − 1 f_{n,2^n-1} fn,2n−1。时间复杂度 O ( n 2 2 n ) O(n^22^n) O(n22n),可以通过。
for(int j = 0;j < (1 << n);j++) {
for(int i = 1;i <= n;i++) {
if(!(j & (1 << (n-i)))) continue;
for(int k = 1;k <= n;k++) {
if((j & (1 << (n-k)))) continue;
f[k][j + (1 << (n-k))] = min(f[k][j + (1 << (n-k))], f[i][j] + dis[i][k]);
}
}
}
2. P2114 [NOI2014] 起床困难综合症
考虑按位计算答案。我们从高到低枚举第几位,对于每一位,计算如果初始时这一位是 0 / 1 0/1 0/1 的最终结果。
然后讨论:
- 如果可以让 0 → 1 0 \to 1 0→1,直接选择 0 0 0。
- 否则,如果可以让 1 → 1 1 \to 1 1→1,那么判断一下是否能用 1 1 1,可以就用。
时间复杂度 O ( n log m ) O(n \log m) O(nlogm)。
for(int i = 30;i >= 0;i--) {
int a = calc(i, 0), b = calc(i, 1);
if(a) ans += (1 << i);
else if(b && (m >= (1 << i))) m -= (1 << i), ans += (1 << i);
} cout << ans << endl;
3. P10449 费解的开关
还没做,,
4. P1593 因子和
先扔个结论:
如果已知一个正整数 n = p 1 a 1 p 2 a 2 … p k a k n=p_1^{a_1}p_2^{a_2}\dots p_k^{a_k} n=p1a1p2a2…pkak,其中 p i p_i pi 为互不相同的质数, a i a_i ai 为正整数,则 n n n 的所有因子之和为 ∏ i = 1 k ∑ j = 0 a i p i j \displaystyle\prod_{i=1}^{k}\sum_{j=0}^{a_i}p_i^j i=1∏kj=0∑aipij。
回到本题。假设 a = p 1 a 1 × p 2 a 2 × … p k a k a=p_1^{a_1}\times p_2^{a_2}\times\dots p_k^{a_k} a=p1a1×p2a2×…pkak,那么显然 a b = p 1 a 1 × b × p 2 a 2 × b × … p k a k × b a^b=p_1^{a_1\times b}\times p_2^{a_2\times b}\times\dots p_k^{a_k\times b} ab=p1a1×b×p2a2×b×…pkak×b。再套用上面结论,有
a n s = ( 1 + p 1 + p 1 2 + ⋯ + p 1 a 1 ) × ( 1 + p 2 + p 2 2 + ⋯ + p 2 a 2 ) × ( 1 + p k + p k 2 + ⋯ + p k a k ) ans=(1+p_1+p_1^2+\dots+p_1^{a_1})\times(1+p_2+p_2^2+\dots+p_2^{a_2})\times(1+p_k+p_k^2+\dots+p_k^{a_k}) ans=(1+p1+p12+⋯+p1a1)×(1+p2+p22+⋯+p2a2)×(1+pk+pk2+⋯+pkak)
然后会发现是等比数列。那么和怎么求呢?这里推导一下:
记 x = 1 + p 1 + p 1 2 + ⋯ + p 1 a 1 x=1+p_1+p_1^2+\dots+p_1^{a_1} x=1+p1+p12+⋯+p1a1。那么将左右两边同时 × p 1 \times p_1 ×p1,得到 x p 1 = p 1 + p 1 2 + p 1 3 + ⋯ + p 1 a 1 + 1 xp_1=p_1+p_1^2+p_1^3+\dots+p_1^{a_1+1} xp1=p1+p12+p13+⋯+p1a1+1。用后面这个式子减去前面的,得到
( p 1 − 1 ) x = p 1 a 1 + 1 − 1 (p_1-1)x=p_1^{a_1+1}-1 (p1−1)x=p1a1+1−1
所以 x = 1 + p 1 + p 1 2 + ⋯ + p 1 a 1 = p 1 a 1 + 1 − 1 p 1 − 1 x=1+p_1+p_1^2+\dots+p_1^{a_1}=\dfrac{p_1^{a_1+1}-1}{p_1-1} x=1+p1+p12+⋯+p1a1=p1−1p1a1+1−1。
这样整个式子就变成了:
a n s = p 1 a 1 + 1 − 1 p 1 − 1 × p 2 a 2 + 1 − 1 p 2 − 1 × ⋯ × p k a k + 1 − 1 p k − 1 ans=\dfrac{p_1^{a_1+1}-1}{p_1-1}\times\dfrac{p_2^{a_2+1}-1}{p_2-1}\times\dots\times\dfrac{p_k^{a_k+1}-1}{p_k-1} ans=p1−1p1a1+1−1×p2−1p2a2+1−1×⋯×pk−1pkak+1−1
这样就可以可以用逆元和快速幂解决。然后,你会发现你获得了 82 ∼ 88 82 \sim 88 82∼88 分()
问题出在逆元部分。逆元的要求是操作数和模数必须互质,但是有可能 ( p − 1 ) m o d 9901 = 0 (p-1) \bmod 9901=0 (p−1)mod9901=0,也就是 p m o d 9901 = 1 p \bmod 9901 = 1 pmod9901=1,此时是没有逆元的。那么此时我们会发现, 1 + p i + p i 2 + ⋯ + p i a i ≡ 1 + 1 + 1 + ⋯ + 1 ( m o d 9901 ) 1+p_i+p_i^2+\dots+p_i^{a_i}\equiv1+1+1+\dots+1\pmod {9901} 1+pi+pi2+⋯+piai≡1+1+1+⋯+1(mod9901),也就是说这个式子和 a i + 1 a_i+1 ai+1 同余。那么统计答案的时候判断一下是否有逆元,如果没有那么就乘上 ( a i + 1 ) (a_i+1) (ai+1) 即可。
这样即可通过。注意,此时有可能你
94
94
94 分,此时记得开 long long
。
signed main() {
int a, b; cin >> a >> b;
if(isp(a)) mp[a] += b; else {
for(int i = 2;i * i <= a;i++) {
if(isp(i) && a % i == 0) {
while(a % i == 0) {
a /= i; mp[i] += b;
}
} if(isp(a)) { mp[a] += b; break; }
}
} int ans = 1; for(auto i : mp) {
if(i.first % mod == 1) { ans = (ans * (i.second+1)) % mod; continue; }
ans = (ans * (power(i.first, i.second+1) - 1 + mod) % mod * power(i.first - 1, mod - 2) % mod);
} cout << ans;
return 0;
}
3. P1842 [USACO05NOV] 奶牛玩杂技
一眼贪心。但是,怎么贪呢?
多试几次可以发现是按照 w + s w+s w+s 从小到大排序,排好序之后从上到下摆放。但是这样为什么对?
假设 w 1 , s 1 w_1,s_1 w1,s1, w 2 , s 2 w_2,s_2 w2,s2 是两头奶牛。这里有 w 1 + s 1 ≤ w 2 + s 2 w_1+s_1\le w_2+s_2 w1+s1≤w2+s2。此处假设没有其他奶牛。
如果 w 1 , s 1 w_1,s_1 w1,s1 在上面,那么第二头的压扁指数为 w 1 − s 2 w_1-s_2 w1−s2。如果 w 2 , s 2 w_2,s_2 w2,s2 在上面,那么第一头的压扁指数为 w 2 − s 1 w_2-s_1 w2−s1。那么因为 w 1 + s 1 ≤ w 2 + s 2 w_1+s_1\le w_2+s_2 w1+s1≤w2+s2,所以(移项就可以得到) w 1 − s 2 ≤ w 2 − s 1 w_1-s_2\le w_2-s_1 w1−s2≤w2−s1,所以第一头奶牛放在上面更优。
至此我们证明了这种贪心是对的。那么排序计算即可,时间复杂度 O ( N log N ) O(N \log N) O(NlogN)。
#include <bits/stdc++.h>
using namespace std;
struct cow {
int w, s; bool operator < (const cow& y) const {
return w + s < y.w + y.s;
}
} a[50005];
int main() {
int n; cin >> n;
for(int i = 1;i <= n;i++) cin >> a[i].w >> a[i].s; sort(a+1, a+n+1);
int sum = 0, ans = -0x7fffffff; for(int i = 1;i <= n;i++) {
ans = max(ans, sum - a[i].s); sum += a[i].w;
} cout << ans << endl;
return 0;
}