Source: ZOJ 2956 - 2964
http://acm.zju.edu.cn/onlinejudge/showProblems.do?contestId=1&pageNumber=20
ZOJ 2956 Another Horizontally Visible Segments
题意:给一些平行y轴的线段,端点都是integer。平行x轴地切一刀,问最多能切多少线段。
分析:坐标都是整点而且范围很小,所以就线段扫描一下就可以。o(n)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int T, n, x, y1, y2; 7 int a[10010]; 8 int main() 9 { 10 scanf("%d", &T); 11 while(T--) 12 { 13 memset(a, 0, sizeof(a)); 14 scanf("%d", &n); 15 for (int i = 0; i < n; i++){ 16 scanf("%d %d %d", &x, &y1, &y2); 17 a[y1] ++; 18 a[y2+1] --; 19 } 20 int cnt = 0, ans = 0; 21 for (int i = 0; i <= 10000; i++){ 22 cnt = cnt + a[i]; 23 if (cnt > ans) ans = cnt; 24 } 25 printf("%d\n", ans); 26 } 27 return 0; 28 }
ZOJ 2957 Concise mathematics problem
题意:一个数列,首项是sinx/x,第二项是它求导,之后是给相邻三项的递推式,给定x和n,问第n项。
分析:精度问题,直接做连样例都过不了。搜题解发现有人有泰勒公式做的=。=。。最后看watashi的代码学了些奇技淫巧。。因为有递推式,所以我们知道了某个相邻的两项就可以推出整个数列,而且我们发现递推式展开,某两项的比值是固定的(虽然不会求= =。)。既然不能从第一项和第二项推出后面,我们就选一个比较远的地方,取一个1和0,然后往前推,推出每一项的值,第i项值记为fi。记所给数列第一项F0,所求第n项记Fn,那么因为比值固定,所以Fn/F0 = fn/f0,所以所求的Fn = F0*fn/f0,即是Fn = fn * sinx / x / f0。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 6 int n; 7 double x, ans; 8 double f[100]; 9 int main() 10 { 11 while(scanf("%d%lf", &n, &x) != EOF) 12 { 13 f[48] = 0; 14 f[47] = 1; 15 for (int i = 47; i > 0; i--) 16 f[i-1] = (2*i + 1) * f[i] / x - f[i+1]; 17 ans = f[n] * sin(x) / x / f[0]; 18 int cnt = 0; 19 while(fabs(ans) < 1.0) 20 ans *= 10.0, cnt++; 21 printf("%.3lf*10^-%d\n", ans, cnt); 22 } 23 return 0; 24 }
ZOJ 2958 Correct the digit
题意:恶心题= =。0-9每个数用3*3的矩阵表示,然后给你一个9个数的序列,数字可能会显示不全,即error。题目保证最多一次error。然后还有一个checksum,问给你的序列的数应该是多少,判断无解和多解,是唯一解输出。(好像写的不太清楚)
分析:也就是说修改的机会只有一次,所以如果有不能识别的数字就只能修改它,否则可以枚举修改每个数字,然后符合checksum的话count++,count为0无解,为1唯一解,超过1就是多解。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<set> 8 #include<map> 9 #include<queue> 10 #include<ctime> 11 #include<string> 12 using namespace std; 13 14 char num[15][3][4] = { 15 { 16 "._.", 17 "|.|", 18 "|_|" 19 }, 20 { 21 "...", 22 "..|", 23 "..|" 24 }, 25 { 26 "._.", 27 "._|", 28 "|_." 29 }, 30 { 31 "._.", 32 "._|", 33 "._|" 34 }, 35 { 36 "...", 37 "|_|", 38 "..|" 39 }, 40 { 41 "._.", 42 "|_.", 43 "._|" 44 }, 45 { 46 "._.", 47 "|_.", 48 "|_|" 49 }, 50 { 51 "._.", 52 "..|", 53 "..|" 54 }, 55 { 56 "._.", 57 "|_|", 58 "|_|" 59 }, 60 { 61 "._.", 62 "|_|", 63 "._|" 64 } 65 }; 66 char a[15][3][4]; 67 char str[5][60]; 68 int digit[15]; 69 70 void trans() 71 { 72 int id; 73 for (int i = 0; i < 3; i++){ 74 for (int j = 0; j < 27; j++){ 75 id = j / 3; 76 a[id][i][j%3] = str[i][j]; 77 } 78 } 79 } 80 bool compare(char a[3][4], char b[3][4]) 81 { 82 for (int i = 0; i < 3; i++) 83 for (int j = 0; j < 3; j++) 84 if (a[i][j] != b[i][j]) return false; 85 return true; 86 } 87 bool can(char a[3][4], char b[3][4]) 88 { 89 for (int i = 0; i < 3; i ++) 90 for (int j = 0; j < 3; j++) 91 if (a[i][j] != '.' && b[i][j] == '.') return false; 92 return true; 93 } 94 int solve() 95 { 96 int ret = 0, cnt = 0; 97 for (int i = 0; i < 9; i++){ 98 bool flag = false; 99 for (int j = 0; j < 10; j++) 100 if (compare(a[i], num[j])){ 101 digit[i] = j; 102 flag = true; 103 break; 104 } 105 if (!flag){ 106 digit[i] = -1; 107 cnt ++; 108 } 109 } 110 //printf("%d\n", cnt); 111 if (cnt >= 2) return 0; 112 if (cnt == 1){ 113 int tmp = 0, pos; 114 for (int i = 0; i < 9; i++){ 115 //printf("%d\n", digit[i]); 116 if (digit[i] != -1) tmp = tmp + digit[i] * (9 - i); 117 else pos = i; 118 } 119 int wrong = 0; 120 for (int j = 0; j < 10; j++) 121 if (can(a[pos], num[j])){ 122 int tmpp = tmp + j * (9 - pos); 123 if (tmpp % 11 == 0){ 124 wrong++; 125 if (wrong == 2) return wrong; 126 digit[pos] = j; 127 } 128 } 129 return wrong; 130 } 131 else{ 132 int tmp = 0; 133 for (int i = 0; i < 9; i++) 134 tmp = tmp + digit[i] * (9 - i); 135 if (tmp % 11 == 0) ret ++; 136 int ori[15]; 137 for (int i = 0; i < 9; i++) ori[i] = digit[i]; 138 for (int i = 0; i < 9; i++){ 139 tmp = 0; 140 for (int j = 0; j < 9; j++) if (j != i) tmp = tmp + ori[j] * (9 - j); 141 for (int j = 0; j < 10; j++){ 142 if (ori[i] != j && can(a[i], num[j])){ 143 int tmpp = tmp + j * (9 - i); 144 if (tmpp % 11 == 0){ 145 ret ++; 146 if (ret == 2) return ret; 147 digit[i] = j; 148 } 149 } 150 } 151 } 152 } 153 return ret; 154 } 155 int main() 156 { 157 while(gets(str[0])) 158 { 159 gets(str[1]); 160 gets(str[2]); 161 trans(); 162 int cnt = solve(); 163 if (cnt == 0) puts("failure"); 164 else if (cnt == 2) puts("ambiguous"); 165 else{ 166 for (int i = 0; i < 9; i++) 167 printf("%d", digit[i]); 168 printf("\n"); 169 } 170 } 171 return 0; 172 }
ZOJ 2960 Re-rejudge
是个dp。待补。
ZOJ 2961 Spinlock
题意:类似于行李箱上的密码锁。有不超过100个按钮,可以让每位转对应的数,给初始状态,问能不能通过某种按钮序列达到目标状态,输出步数最小时字典序最小的解。
分析:因为锁的位数不超过6,也就是100万的状态,然后就是bfs。类似于之前一道状压+spfa的题,直接连边估计要跪,每次对当前状态就枚举所有按钮,o(100)地转移。字典序最小就是排一下序。
代码写得比较挫。开始忘记考虑字典序的问题,其实读入可以直接读入字符串。压缩和解码写得麻烦了。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<set> 8 #include<map> 9 #include<queue> 10 #include<ctime> 11 #include<string> 12 using namespace std; 13 14 struct edge{ 15 char s[10]; 16 int w; 17 } e[300]; 18 struct queue{ 19 int u, p, s, e; 20 } q[1003000]; 21 int wei, cas, d, n, pos, st, ed; 22 int vis[1003000]; 23 void output(int pos) 24 { 25 if (q[pos].p == -1) return; 26 output(q[pos].p); 27 printf("%s\n", e[q[pos].e].s); 28 } 29 int trans(int u, int x) 30 { 31 int ret = 0, v = e[x].w; 32 int tt[10]; 33 for (int i = d-1; i >= 0; i--){ 34 tt[i] = (u + v) % 10; 35 u /= 10, v /= 10; 36 } 37 for (int i = 0; i < d; i++) 38 ret = ret * 10 + tt[i]; 39 return ret; 40 } 41 int bfs(int st, int ed) 42 { 43 if (st == ed){ 44 return 0; 45 } 46 int head, tail; 47 head = tail = 0; 48 vis[st] = cas; 49 q[tail].u = st; 50 q[tail].p = -1; 51 q[tail++].s = 0; 52 while(head < tail) 53 { 54 int u = q[head].u; 55 for (int i = 0; i < n; i++){ 56 int v = trans(u, i); 57 if (vis[v] == cas) continue; 58 vis[v] = cas; 59 q[tail].e = i; 60 q[tail].u = v; 61 q[tail].s = q[head].s + 1; 62 q[tail++].p = head; 63 if (v == ed){ 64 pos = tail - 1; 65 return q[head].s + 1; 66 } 67 68 } 69 head ++; 70 } 71 return -1; 72 } 73 void init(int x) 74 { 75 int tt[10]; 76 int tmp = e[x].w; 77 for (int i = d-1; i >= 0; i--){ 78 tt[i] = tmp % 10; 79 tmp /= 10; 80 } 81 for (int i = 0; i < d; i++){ 82 e[x].s[i] = tt[i] + '0'; 83 } 84 e[x].s[d] = '\0'; 85 } 86 bool cmp(edge a, edge b) 87 { 88 return strcmp(a.s, b.s) < 0; 89 } 90 int main() 91 { 92 cas = 0; 93 memset(vis, 0, sizeof(vis)); 94 while(scanf("%d %d", &d, &n) != EOF) 95 { 96 cas ++; 97 for (int i = 0; i < n; i++){ 98 scanf("%d", &e[i].w); 99 init(i); 100 } 101 sort(e, e+n, cmp); 102 // for (int i = 0; i < n; i++) 103 // printf("%s\n", e[i].s); 104 scanf("%d %d", &st, &ed); 105 int ans = bfs(st, ed); 106 printf("%d\n", ans); 107 if (ans != -1 && ans != 0) output(pos); 108 } 109 return 0; 110 }
ZOJ 2962 Stack By Stack
题意:n个栈,开始全空。第一个栈压入1,2..,cap[1]的数,然后满了,全部倒入第二个栈,如果第二个栈容量小,第一个栈剩余的就清空,否则就再把第一个栈填满,倒入第二个栈。第二个栈满了再倒入第三个栈,一直做下去,直到所有栈满。栈底标号1,然后往上递增。问第n个栈标号x到y的区间的数的和。
分析:挺有意思的题。我们会发现栈里的数肯定都是一段一段连续的。而且给定栈和每个栈容量后,每个位置的数也确定了。而且栈的问题肯定多想想递归,cal(i, j)表示第i个栈里标号从1到j的数的和。ans = cal(n, y) - cal(n, x-1)。考虑第i个栈,最底下一段,如果长度大于i-1的栈,那么肯定是前一个栈的几次循环加上顶上的一段,如果小于,那么就只有前一个栈顶上的一段,然后就可以递归求解了。递归出口就是第1个栈,即1,2,3..x的求和。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 typedef long long LL; 6 int n; 7 LL x, y; 8 LL v[1010]; 9 LL sum[1010]; 10 LL cal(int index, LL pos) 11 { 12 if (index == 0) return (pos+1ll)*pos/2ll; 13 if (pos % v[index-1] == 0) return sum[index-1] * (pos / v[index-1]); //这里必须先算后面,不然wa,好像是超longlong 14 else return sum[index-1] * (pos/v[index-1] + 1) - cal(index-1, v[index-1] - pos%v[index-1]); //前一个栈的几倍,上取整,减去除开最顶上一段的部分 15 } 16 int main() 17 { 18 while(scanf("%d", &n) != EOF) 19 { 20 for (int i = 0; i < n; i ++){ 21 scanf("%lld", v+i); 22 sum[i] = cal(i, v[i]); //把每个栈的总和先处理了 23 } 24 scanf("%lld %lld", &x, &y); 25 printf("%lld\n", cal(n-1,y) - cal(n-1, x-1)); 26 } 27 return 0; 28 }
ZOJ 2963 Treasure Hunter
好像是dijkstra预处理,然后状压dp。待补。
ZOJ 2964 Triangle
题意:给互质的a和z,找不相同的l,m,n,满足三角形的关系(两边之和大于第三边),满足a^l和a^m和a^n关于z同余(等价于题目中除以z的小数部分相同),输出min(l+m+n)
分析:数学渣的痛。尝试着分析。因为同余,提到左边,所以就是求delta,a^delta ≡ 1 (mod z),delta为两项的差。暴力枚举delta肯定会跪。因为a与z互质,这个delta可以证明是且仅是z的欧拉函数的约数(超过z也可以满足,但是显然不优,具体我就没考虑了)。因为我们要最小的周长,所以枚举z的欧拉函数的约数p,找到最小的p满足a^p ≡ 1 (mod z)即可。所以三项会是x, x+p, x+2p,所以x+x+p > x+2p,即x>p,所以x=p+1,所求即6p+3。
(//这一段放弃治疗了。。逻辑不对。。
然后证明一下delta是且仅是phi(z)的约数。a=1的情况delta取1。除开a=1,phi(z) = pq,则(a^p)^q ≡ 1 (mod z),这样就有可能a^p = 1,这个就是说phi(z)的约数可能可以是delta,好像没什么证明的必要。。主要是反过来,也就是证明delta只能是phi(z)的约数。假设gcd(x, phi(z)) = y ≠ x且a^x ≡ 1 (mod z),则mx + nphi(z) = y,则有a^(mx+nphi(z)) ≡ (a^x)^m ≡ 1 不同余 1 ≡ a^x (mod z), 所以矛盾,即gcd(x, phi(z)) = x。)
我们还是证明弱一些的断言,只证最小循环节是phi(z)的约数。同余1是有一个循环节的,即a^0,a^x,a^2x..,设最小的循环节是x,显然它小于等于phi(z),我们要证明的是x是phi(z)的约数。假设x不是phi(z)的约数,则gcd(x, phi(z)) = y < x,即mx+nphi(z) = y,则a^y ≡ 1 (mod z),y<x,但x是最小的循环节,所以矛盾,x是phi(z)的约数。
代码是学习的=。=这个欧拉函数和线性筛都写得很好哎。phi(x) = x * (1 - 1/p1) .. * (1 - 1/pi) .. * (1 - 1/pn),即∏pi^(ci-1)∏(pi-1)。线性筛的理解就是每个合数只被最小的质因数筛掉。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 const int ps = 100001; 6 int mpf[ps+5], prime[ps+5]; 7 int pn = 0; 8 int T, a, z; 9 10 void getprime(){ 11 memset(mpf, 0, sizeof(mpf)); 12 for (int i = 2; i < ps; i ++){ 13 if (mpf[i] == 0) prime[pn++] = i; 14 for (int j = 0; j < pn && i*prime[j] < ps && (prime[j] <= mpf[i]||mpf[i] == 0); j++) 15 mpf[i*prime[j]] = prime[j]; 16 } 17 } 18 int phi(int x){ 19 int ret = 1; 20 for (int i = 0; i < pn; i++){ 21 if (x % prime[i] == 0){ 22 int tmp = prime[i] - 1; 23 x /= prime[i]; 24 while(x % prime[i] == 0){ 25 tmp *= prime[i]; 26 x /= prime[i]; 27 } 28 ret *= tmp; 29 } 30 } 31 if (x > 1){ 32 ret *= x - 1; 33 } 34 return ret; 35 } 36 int pow_mod(int a, int n){ 37 if (n == 0) return 1; 38 if (n == 1) return a % z; 39 int tmp = pow_mod(a, n/2); 40 if (n & 1) return (long long) tmp * tmp % z * a % z; 41 return (long long) tmp * tmp % z; 42 } 43 int cal(int a, int x){ 44 int ans = x; 45 for (int i = 1; i * i <= x; i++){ 46 if (x % i == 0){ 47 int tmp = pow_mod(a, i); 48 if (tmp == 1) return i; 49 tmp = pow_mod(a, x/i); 50 if (tmp == 1) ans = x/i; 51 } 52 } 53 return ans; 54 } 55 int main() 56 { 57 getprime(); 58 scanf("%d", &T); 59 while(T--) 60 { 61 scanf("%d %d", &a, &z); 62 int ph = phi(z); 63 ph = cal(a, ph); 64 printf("%lld\n", (long long)ph * 6 + 3); 65 } 66 return 0; 67 }