ZOJ Monthly, May 2008 部分题目解题报告

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 }
View Code

 

 

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 }
View Code

 

 

 

 

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 }
View Code

 

 

 

 

 

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 }
View Code

 

 

 

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 }
View Code

 

 

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 }
View Code

 

转载于:https://www.cnblogs.com/james47/p/3930313.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值