题意:
无向图,修公路,第i个城市的值是a[i],第j个城市的值是a[j],在他们之间修公路花费gcd(a[i] , a[j]),求修公路连接所有n个城市的最小花费
思路:(MST)
最小生成树问题,对kruskal进行变形,但由于这题范围太大,不能暴力枚举所有任意两个数的最大公约数,所以要考虑素数分布,然后我们再来看题面:n个点的最小生成树,边权是gcd(a[i] , a[j]),
当n很大时(差不多大于1000时),就肯定存在素数,边权就都是1,得出的最小生成树值就是n - 1。(结论直接用),在n小于1000时正常跑kruskal
另外还有一种情况,就是L = R时,说明边权都是L,得出的最小生成树就是(n - 1) * L,并且这里注意开long long
由于平常的kruskal算法都是直接给了说是有m条边,但这个题没给,所以不知道总边数,那么可以开一个idx表示总边数。
//本题示例
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { int w = gcd(a[i], a[j]); edge[idx ++ ] = {i , j , w}; }
1 #include <iostream> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <vector> 8 #include <map> 9 #include <set> 10 11 #define x first 12 #define y second 13 14 using namespace std; 15 16 typedef long long LL; 17 typedef pair<int, int>PII; 18 19 const int N = 100010; 20 21 int n, L, R, a[200001]; 22 int idx; 23 int p[N]; 24 struct Edge 25 { 26 int a, b, w; 27 bool operator < (const Edge& t) const 28 { 29 return w < t.w; 30 } 31 }edge[N]; 32 33 unsigned long long seed; 34 unsigned long long xorshift64() 35 { 36 unsigned long long x = seed; 37 x ^= x << 13; 38 x ^= x >> 7; 39 x ^= x << 17; 40 return seed = x; 41 } 42 int gen() 43 { 44 return xorshift64() % (R - L + 1) + L; 45 } 46 47 int gcd(int a, int b) 48 { 49 int c = a % b; 50 while (c) 51 { 52 a = b; 53 b = c; 54 c = a % b; 55 } 56 return b; 57 } 58 59 int find(int x) 60 { 61 if (p[x] != x) p[x] = find(p[x]); 62 return p[x]; 63 } 64 65 LL kruskal() 66 { 67 sort(edge + 1, edge + 1 + idx); 68 LL res = 0, cnt = 0; 69 70 for (int i = 1; i <= idx; i++) 71 { 72 int a = edge[i].a, b = edge[i].b, w = edge[i].w; 73 74 a = find(a), b = find(b); 75 if (a != b) 76 { 77 p[a] = b; 78 res += w; 79 cnt++; 80 } 81 if (cnt == n - 1) break; 82 } 83 return res; 84 } 85 86 int main() 87 { 88 scanf("%d%d%d%llu", &n, &L, &R, &seed); 89 for (int i = 1; i <= n; i++) a[i] = gen(); 90 91 if (L == R) 92 { 93 cout << (LL)(n - 1) * L << endl;//化为long long,不然有个点过不了 94 return 0; 95 } 96 if (n >= 1000) 97 { 98 cout << n - 1 << endl; 99 return 0; 100 } 101 //n < 1000
102
103 for (int i = 1; i <= n; i++) p[i] = i; 104 105 for (int i = 1; i <= n; i++) 106 for (int j = 1; j <= n; j++) 107 { 108 int w = gcd(a[i], a[j]); 109 edge[idx ++ ] = {i , j , w}; 110 } 111 112 cout << kruskal() << endl; 113 114 return 0; 115 }
题意:
二维空间里放了n个盒子,有水平往左和竖直往下两种重力,求重力作用之后形成的轮廓周长。
思路:(模拟)(思维)
首先每次输入一个方块,周长就加4,然后判断平移之后挨着别的方块,这里拿向左平移举例子,当输入一个新的方块后,当这一行原来有别的方块时,平移之后肯定会左右挨着,这时就去掉两条边的
长度:
另外的情况就是当输入一个新方块后,向左平移之后上下挨着别的方块:
因为上下挨着两种情况对称,考虑下面挨着的情况,那么上面挨着的情况就照葫芦画瓢就可以了,那么这种情况一定要想清楚,假如记这一行是x[xx],下面一行是x[xx - 1],判断的是新输入的这个方块是否
有下面挨着的方块,假如上面一行右边那个方块是新输入的方块,那么很显然平移之后它下面没有挨着的方块,而假如上面一行左边那个方块是新输入的方块,那么它就有下面挨着的方块,我们分析一下,
可以发现当下面一行的方块数大于等于这一行的方块数时,新输入的这个方块就有下面挨着的方块,那么答案就去掉两条边的长度。
上面挨着的情况同理。
上面的图的情况是新输入的方块没有挨着的
那么新输入的方块下面挨着别的方块的情况再举个例子:
然后向下平移的情况就对着向左平移的情况照葫芦画瓢就完了
1 #include <iostream> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <vector> 8 #include <map> 9 #include <set> 10 11 #define x first 12 #define y second 13 14 using namespace std; 15 16 typedef long long LL; 17 typedef pair<int, int>PII; 18 19 const int N = 200010; 20 21 int n; 22 int x[N], y[N]; 23 LL res1, res2; 24 25 int main() 26 { 27 cin >> n; 28 for (int i = 1; i <= n; i++) 29 { 30 int xx, yy; 31 cin >> xx >> yy; 32 res1 += 4, res2 += 4; 33 34 if (x[xx]) res1 -= 2; 35 x[xx] ++; 36 if (x[xx - 1] >= x[xx]) res1 -= 2; 37 if (x[xx + 1] >= x[xx]) res1 -= 2; 38 39 40 if (y[yy]) res2 -= 2; 41 y[yy] ++; 42 if (y[yy - 1] >= y[yy]) res2 -= 2; 43 if (y[yy + 1] >= y[yy]) res2 -= 2; 44 45 46 cout << res1 << ' ' << res2 << endl; 47 } 48 49 return 0; 50 }
题意:
一个人去打怪兽,当他打过这个怪兽之后,会损失一定的生命值和疲劳值,并获得一定数量的金币,如果生命值减为零了,人就死了,而如果疲劳值减为零了,就相应的从生命值里扣,问保证这个人活着的前提下,能获得的金币的最大值是多少。
思路:(dp)
首先考虑结构体排序用贪心来做,但在分析的过程中我们就发现要考虑的条件实在是太多了,所以基本不太可能用贪心了。然后就考虑dp,发现dp是可以的,可以看出这个题是01背包的变形,即遇到一个怪兽,你是打还是不打,对应01背包中一个物品选还是不选的情况。
由于这个题是三维的,所以如果不优化的话只能过不到百分之四十的样例,所以我们最后优化掉 i 这一维空间就可以了,注意关键点就是 j 这一维,就是生命值这一维,循环时是大于h[i]的(H到h[i]),
如果用集合角度考虑这个dp问题的话(闫氏dp分析法),即为这个人在生命值大于0时,可以获得的最大金币数。
最后注意开 long long
1 #include <iostream> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <vector> 8 #include <map> 9 #include <set> 10 11 #define x first 12 #define y second 13 14 using namespace std; 15 16 typedef long long LL; 17 typedef pair<int, int>PII; 18 19 const int N = 1010; 20 21 LL n, H, S; 22 LL h[N], s[N], w[N]; 23 LL f[310][310]; 24 25 int main() 26 { 27 cin >> n >> H >> S; 28 29 for (int i = 1; i <= n; i++) cin >> h[i] >> s[i] >> w[i]; 30 31 LL res = 0; 32 for (int i = 1; i <= n; i++) 33 for (int j = H; j > h[i]; j--) 34 { 35 for (int k = S; k >= 0; k--) 36 { 37 if (k >= s[i]) f[j][k] = max(f[j][k], f[j - h[i]][k - s[i]] + w[i]); 38 else 39 { 40 if (j - h[i] - (s[i] - k) > 0) 41 f[j][k] = max(f[j][k], f[j - h[i] - (s[i] - k)][0] + w[i]); 42 43 } 44 45 res = max(res, f[j][k]); 46 } 47 } 48 49 cout << res << endl; 50 51 return 0; 52 }
题意:
给定 一个0/1矩阵C ,构造两个矩阵 A , B ,其中 1 形成了完整的不分散的一块四连通块,并且对于 C 中所有位置,若是 1,则 A , B 对应位置必须都是 1,否则 A , B 之中必须有一个这个位置为 0 。 保证C阵的边框都是0 。
思路:(思维)
这题真的读懂题意是关键,因为这题说C的边界都是0,所以我们可以让A的最左边一列都是 1 ,B的最右边一列都是 1 ,然后行分奇偶,假如A的奇数行染1 , B的偶数行染1,那么这样一定保证所有的1都是连通的。
注意的点就是存矩阵c的时候用char ,不要用int , 因为存的是某一个点的东西
1 #include <iostream> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <vector> 8 #include <map> 9 #include <set> 10 11 #define x first 12 #define y second 13 14 using namespace std; 15 16 typedef long long LL; 17 typedef pair<int, int>PII; 18 19 const int N = 510; 20 21 int n, m; 22 char c[N][N]; 23 24 int main() 25 { 26 cin >> n >> m; 27 for (int i = 1; i <= n; i++) 28 for (int j = 1; j <= m; j++) 29 cin >> c[i][j]; 30 31 for (int i = 1; i <= n; i++)//A矩阵 32 { 33 for (int j = 1; j <= m; j++) 34 if (i == 1 || (j % 2 == 1 && i != n)) cout << 1; 35 else cout << c[i][j]; 36 37 cout << endl; 38 } 39 40 for (int i = 1; i <= n; i++)//B矩阵 41 { 42 for (int j = 1; j <= m; j++) 43 if (i == n || (j % 2 == 0 && i != 1)) cout << 1; 44 else cout << c[i][j]; 45 46 cout << endl; 47 } 48 49 return 0; 50 }