2016多校7.14 Warmup 题解

  先讲1007,是一个数位dp,询问一个区间内,各位数的和是一个素数的数字的个数。其实我并不会数位dp,这题直接套用了上次多校lyf队长的dp代码,改了点返回参数没想到直接AC了。代码如下:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <iostream>
 5 #include <vector>
 6 #include <queue>
 7 #include <math.h>
 8 using namespace std;
 9 typedef long long ll;
10 
11 int bit[20];
12 ll dp[20][200];
13 
14 bool isprime[200];
15 
16 void getPrimeTable()
17 {
18     memset(isprime,1,sizeof(isprime));
19     isprime[1]=0;
20     for(int i=2;i<200;i++)
21     {
22         if(isprime[i])
23         {
24             for(int j=2*i;j<200;j+=i)
25             {
26                 isprime[j]=false;
27             }
28         }
29     }
30 }
31 
32 ll dfs(int pos,int sum,bool flag)
33 {
34     if(pos == -1) return (ll)isprime[sum];
35     ll& ans = dp[pos][sum];
36     if(flag && ans!=-1) return ans;
37     int d = flag?9:bit[pos];
38 
39     ll ret = 0;
40     for(int i=0;i<=d;i++)
41     {
42         ret += dfs(pos-1,sum+i,flag||i<d);
43     }
44     if(flag) ans = ret;
45     return ret;
46 }
47 
48 ll solve(ll x)
49 {
50     int pos = 0;
51     while(x)
52     {
53         bit[pos++] = x % 10;
54         x /= 10;
55     }
56 
57     ll ans = 0;
58     ans += dfs(pos-1,0,false);
59     return ans;
60 }
61 
62 int main()
63 {
64     getPrimeTable();
65     int T;
66     scanf("%d",&T);
67     memset(dp,-1,sizeof(dp));
68     for(int kase=1;kase<=T;kase++)
69     {
70         ll x,y;
71         scanf("%I64d%I64d",&x,&y);
72         printf("%I64d
",solve(y)-solve(x-1));
73     }
74 }
View Code

从这个来看,看来以后对于各个位数上的和能够满足某一条件的数位dp,都可以直接套用这个模板了。顺便给下上次那个数位dp的AC代码。那题的题意是求区间上满足各位数上的和能被这个数整除的数的个数。代码如下:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <iostream>
 5 #include <vector>
 6 #include <queue>
 7 #include <math.h>
 8 using namespace std;
 9 typedef long long ll;
10 
11 int bit[12],dp[12][82][82][82];
12 
13 int dfs(int pos,int sum,int res,int mod,bool flag)
14 {
15     if(pos == -1) return sum==mod && res==0;
16     int& ans = dp[pos][sum][res][mod];
17     if(flag && ans!=-1) return ans;
18     int d = flag?9:bit[pos];
19 
20     int ret = 0;
21     for(int i=0;i<=d;i++)
22     {
23         ret += dfs(pos-1,sum+i,(res*10+i)%mod,mod,flag||i<d);
24     }
25     if(flag) ans = ret;
26     return ret;
27 }
28 
29 int solve(int x)
30 {
31     int pos = 0;
32     while(x)
33     {
34         bit[pos++] = x % 10;
35         x /= 10;
36     }
37     
38     int ans = 0;
39     for(int i=1;i<=81;i++) ans += dfs(pos-1,0,0,i,false);
40     return ans;
41 }
42 
43 int main()
44 {
45     int T;
46     scanf("%d",&T);
47     memset(dp,-1,sizeof(dp));
48     for(int kase=1;kase<=T;kase++)
49     {
50         int x,y;
51         scanf("%d%d",&x,&y);
52         printf("Case %d: %d
",kase,solve(y)-solve(x-1));
53     }
54 }
View Code

  1004,是一个模拟题,只要搞懂了题意就行,还好队友玩过了炉石传说,所以这题顺理成章的很快就1A了。要注意的是,mana值是没用的,有护甲的时候先扣护甲,再扣血;血有上限为30,护甲没有,在你我这样的一个大回合里,每个回合扣血数加1。具体见代码(现场上交的,没做过修改):

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <iostream>
 5 #include <vector>
 6 #include <queue>
 7 #include <math.h>
 8 using namespace std;
 9 typedef long long ll;
10 
11 struct player
12 {
13     int hp,r;
14     int id;
15 }G[3];
16 
17 
18 void sub(int pl,int down)
19 {
20     if(G[pl].r>=down) G[pl].r-=down;
21     else
22     {
23         G[pl].hp-=(down-G[pl].r);
24         G[pl].r=0;
25     }
26 }
27 
28 void usepow(int pl)
29 {
30     int id = G[pl].id;
31     if(id == 1 || id==2)
32     {
33         sub(3-pl,id);
34         //G[3-pl].hp-=id;
35     }
36     else if(id == 3)
37     {
38         G[pl].r+=2;
39     }
40     else
41     {
42         G[pl].hp+=2;
43         if(G[pl].hp>30) G[pl].hp=30;
44     }
45 }
46 
47 
48 
49 
50 int main()
51 {
52     int T;
53     scanf("%d",&T);
54     while(T--)
55     {
56         for(int i=1;i<=2;i++)
57             scanf("%d%d%d",&G[i].id,&G[i].hp,&G[i].r);
58         int cnt = 1;
59         int flag = 1;
60         for(;;)
61         {
62             sub(1,cnt);
63             //G[1].hp-=cnt;
64             if(G[1].hp<=0)
65             {
66                 flag = 0;
67                 break;
68             }
69             usepow(1);
70             if(G[1].hp<=0)
71             {
72                 flag = 0;
73                 break;
74             }
75 
76             sub(2,cnt);
77             //G[2].hp-=cnt;
78             if(G[2].hp<=0)
79             {
80                 //flag = 0;
81                 break;
82             }
83             usepow(2);
84             if(G[2].hp<=0)
85             {
86                 //flag = 0;
87                 break;
88             }
89             cnt++;
90         }
91         puts(flag?"YES":"NO");
92     }
93 }
View Code

  1008是一个按照税法缴税的水题,但是当时题目数据给错了,所以WA倒一片。有意思的是我当时去百度了一下,找到了缴税百分比的表格,却没有发现最后一个数据是不同的!!- -,这题也真是有趣啊。。代码如下(大力学长的代码):

 1 #include <stdio.h>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <set>
 5 #include <math.h>
 6 #include <vector>
 7 #include <stack>
 8 #include <map>
 9 #include <string.h>
10 #define t_mid (l+r>>1)
11 #define ls (o<<1)
12 #define rs (o<<1 | 1)
13 #define lson ls,l,t_mid
14 #define rson rs,t_mid+1,r
15 const double eps = 1e-10;
16 using namespace std;
17 typedef long long ll;
18 int T;
19 double x;
20 double money[10] = {0,1500,4500,9000,35000,55000,80000};
21 double rate[10] = {0.03,0.1,0.2,0.25,0.3,0.35,0.45};
22 int sgn(double x){ return x < -eps ? -1 : x > eps ? 1 : 0;}
23 int main(){
24     cin >> T;
25     while(T--){
26         scanf("%lf",&x);
27         double amount = 0;
28         x -= 3500.0;
29         int pos = 1;
30         while(sgn(x) > 0 && pos <= 6){
31             if(x > money[pos]){
32                 amount += (money[pos] - money[pos-1]) * rate[pos-1];
33                 pos ++;
34             }else{
35                 amount += (x - money[pos-1]) * rate[pos-1];
36                 pos ++;
37                 break;
38             }
39         }
40         if(sgn(x - 80000.0) > 0)
41             amount += (x - 80000.0) * 0.45;
42         printf("%.2f
",amount);
43     }
44     return 0;
45 }
View Code

  

  1006是一个很烦的题目,题意是给出若干个点,问存在多少个矩形,它们的边都是水平或者垂直的,且四个角上都包含了点。思路是,先想办法存下离散化后的图,然后对每一列,往下推m-1行,分别找这两行往后推m-1列的纵坐标的值是否相等,若相等,则个数加1。具体见代码(有注释):

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #include <queue>
  5 #include <vector>
  6 #include<iostream>
  7 using namespace std;
  8 const int N = 101000;
  9 const int inf = 0x3f3f3f3f;
 10 typedef pair<int,int> pii;
 11 typedef long long ll;
 12 
 13 struct point
 14 {
 15     int x,y;
 16 };
 17 point po[N];
 18 
 19 vector<int> G[N];
 20 vector<int> RG[N];
 21 
 22 bool cmp1(point a,point b) {return a.x==b.x?a.y<b.y:a.x<b.x;}
 23 bool cmp2(point a,point b) {return a.y==b.y?a.x<b.x:a.y<b.y;}
 24 
 25 /*
 26     xx,yy,代表当前坐标
 27     返回这个坐标同一个横坐标上面第m个点的纵坐标
 28 */
 29 int Find(int xx,int yy,int m,int mx)
 30 {
 31     int L = 0,R = mx,mid;
 32     while(L<=R)
 33     {
 34         mid = R + L >> 1;
 35         if(RG[mid][0] < xx)
 36             L = mid + 1;
 37         else if(RG[mid][0] > xx) R = mid - 1;
 38         else break;
 39     }
 40 
 41     int now = mid;  // now是找到的当前行
 42     if(L > R) return inf;
 43     L = 1;
 44     R = RG[now].size() - 1;
 45 
 46     while(L<=R)
 47     {
 48         mid = R + L >> 1;
 49         if(RG[now][mid]<yy)
 50             L = mid + 1;
 51         else if(RG[now][mid]>yy) R = mid - 1;
 52         else break;
 53     }
 54     if(mid + m-1 <= RG[now].size()-1)
 55         return RG[now][mid+m-1];
 56     else return inf;
 57 }
 58 
 59 int main()
 60 {
 61     int t;
 62     cin >> t;
 63     while(t--)
 64     {
 65         int n,m;
 66         scanf("%d%d",&n,&m);
 67         for(int i = 0;i < n;i ++)
 68         {
 69             G[i].clear();
 70             RG[i].clear();
 71         }
 72 
 73         for(int i= 0;i <n;i ++) scanf("%d%d",&po[i].x,&po[i].y);
 74 
 75         if(m <= 1)
 76         {
 77             cout << 0 << endl;
 78         }
 79         else
 80         {
 81             sort(po,po+n,cmp1);
 82             /*
 83                 cmp1是按照x的大小排序
 84             */
 85             int cnt = 0;
 86             /*
 87                 RG里面放的是某一行里面的所有y值,
 88                 同时,第一个元素是这一行的x值是多少
 89             */
 90             RG[cnt].push_back(po[0].x);
 91             RG[cnt].push_back(po[0].y);
 92             for(int i= 1;i < n;i ++)
 93             {
 94                 point now = po[i];
 95                 if(now.x > RG[cnt][0])
 96                 {
 97                     cnt++;
 98                     RG[cnt].push_back(po[i].x);
 99                     RG[cnt].push_back(po[i].y);
100                 }
101                 else
102                 {
103                     RG[cnt].push_back(po[i].y);
104                 }
105             }
106 
107             /*
108                 G里面存放的元素和RG正好相反,
109                 存某一列里面的所有x值,
110                 同时,第一个元素是这一列的y值是多少
111             */
112             sort(po,po+n,cmp2);
113             int cnt2 = 0;
114             G[0].push_back(po[0].y);
115             G[0].push_back(po[0].x);
116             for(int i= 1;i < n;i ++)
117             {
118                 point now = po[i];
119                 if(now.y > G[cnt2][0])
120                 {
121                     cnt2 ++;
122                     G[cnt2].push_back(now.y);
123                     G[cnt2].push_back(now.x);
124                 }
125                 else
126                 {
127                     G[cnt2].push_back(now.x);
128                 }
129             }
130             int sum= 0;
131             for(int i= 0;i + m-1 <= cnt2;i ++)
132             {
133                 int yy = G[i][0]; //yy表示的是第i列的y值是多少
134                 for(int j = 1;j + m - 1< G[i].size();j ++)
135                 {
136                     //j从1开始,是因为0是用来存放y值的,而不是这一列变化的x值
137                     int xx = G[i][j]; //这一列的第j个的x值
138                     int mm = Find(xx,yy,m,cnt); // 在当前行上找后面的第m-1个位置是否存在,
139                                                 // 存在的话返回的是那个位置的纵坐标的值
140                     int nn = Find(G[i][j+m-1],yy,m,cnt); // 往下m-1行后面的第m-1个位置是否存在
141                     if(mm!= inf&& mm == nn) // 返回inf表示的是不存在
142                     {
143                         sum++;
144                     }
145                 }
146             }
147             cout << sum << endl;
148         }
149     }
150     return 0;
151 }
View Code

这题是队友做出来的,这里放一下他的源代码(我们的二分写法不太一样- -,队友的数据结构真心强。躺了。。):

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #include <queue>
  5 #include <vector>
  6 #include<iostream>
  7 using namespace std;
  8 const int N = 101000;
  9 const int inf = 0x3f3f3f3f;
 10 typedef pair<int,int> pii;
 11 typedef long long ll;
 12 
 13 struct point
 14 {
 15     int x,y;
 16 };
 17 point po[N];
 18 
 19 vector<vector<int> > G(N);
 20 vector<vector<int> > RG(N);
 21 
 22 bool cmp1(point a,point b)
 23 {
 24     if(a.x<b.x) return 1;
 25     else if(a.x > b.x) return 0;
 26     else
 27     {
 28         if(a.y < b.y) return 1;
 29          return 0;
 30     }
 31 }
 32 
 33 
 34 bool cmp2(point a,point b)
 35 {
 36     if(a.y<b.y) return 1;
 37     else if(a.y > b.y) return 0;
 38     else
 39     {
 40         if(a.x <= b.x) return 1;
 41          return 0;
 42     }
 43 }
 44 
 45 int Find(int xx,int yy,int m,int mx)
 46 {
 47     int L = 0,R=mx+1;
 48     while(R-L>1)
 49     {
 50         int mid = (R+L) /2;
 51         if(RG[mid][0] <= xx)
 52             L = mid;
 53         else R = mid;
 54     }
 55     int now = L;
 56     if(RG[now][0] != xx) return 100000000;
 57     L = 1;
 58     R = RG[now].size();
 59 
 60     while(R-L>1)
 61     {
 62         int mid = (R+L)/2;
 63         if(RG[now][mid] <= yy)
 64             L = mid;
 65         else R = mid;
 66     }
 67     if(L + m-1 < RG[now].size())
 68         return RG[now][L+m-1];
 69     else return 100000000;
 70 }
 71 
 72 
 73 
 74 
 75 
 76 
 77 int main()
 78 {
 79     int t;
 80     cin >> t;
 81     while(t--)
 82     {
 83         int n,m;
 84         scanf("%d%d",&n,&m);
 85         for(int i = 0;i < n;i ++)
 86         {
 87             G[i].clear();
 88             RG[i].clear();
 89         }
 90 
 91         for(int i= 0;i <n;i ++)
 92             scanf("%d%d",&po[i].x,&po[i].y);
 93         if(m <= 1)
 94         {
 95             cout << 0 << endl;
 96         }
 97         else
 98         {
 99             sort(po,po+n,cmp1);
100             int cnt = 0;
101             RG[cnt].push_back(po[0].x);
102             RG[cnt].push_back(po[0].y);
103             for(int i= 1;i < n;i ++)
104             {
105                 point now = po[i];
106                 if(now.x > RG[cnt][0])
107                 {
108                     cnt++;
109                     RG[cnt].push_back(po[i].x);
110                     RG[cnt].push_back(po[i].y);
111                 }
112                 else
113                 {
114                     RG[cnt].push_back(po[i].y);
115                 }
116             }
117             sort(po,po+n,cmp2);
118             int cnt2 = 0;
119             G[0].push_back(po[0].y);
120             G[0].push_back(po[0].x);
121             for(int i= 1;i < n;i ++)
122             {
123                 point now = po[i];
124                 if(now.y > G[cnt2][0])
125                 {
126                     cnt2 ++;
127                     G[cnt2].push_back(now.y);
128                     G[cnt2].push_back(now.x);
129                 }
130                 else
131                 {
132                     G[cnt2].push_back(now.x);
133                 }
134             }
135             int sum= 0;
136             for(int i= 0;i <= cnt2;i ++)
137             {
138                 int yy = G[i][0];
139                 if(i + m-1 > cnt2) break;
140                 for(int j = 1;j + m - 1< G[i].size();j ++)
141                 {
142                     int xx = G[i][j];
143                     int mm = Find(xx,yy,m,cnt);
144                     int nn = Find(G[i][j+m-1],yy,m,cnt);
145                     //cout << xx << ' ' <<  yy  << ' ' << nn << ' ' << mm << endl;
146                     if(mm!= 100000000&& mm == nn)
147                     {
148                         //cout << xx <<' ' << yy << endl;
149                         sum++;
150                     }
151 
152                 }
153             }
154             cout << sum << endl;
155         }
156 
157     }
158     return 0;
159 }
160 /*
161 12
162 12 2
163 1 1
164 2 1
165 3 1
166 4 1
167 5 1
168 1 2
169 2 2
170 3 2
171 4 2
172 5 2
173 3 3
174 4 3
175 
176 
177 -2 1
178 -2 -1
179 -1 1
180 -1 -1
181 -2 -2
182 -1 -2
183 1 1
184 1 -1
185 2 1
186 2 -1
187 */
View Code

  然后是1003,也是队友的奥义思路,然后结合了大力的大数java搞出来的。这题的题意是,给出n个操作,对自然数序列(1,2,3,4...)每个操作保留A[i]个数,删除A[i]个数。问n个操作以后剩下的第n个数是多少。思路是,反推,比方说当前这个数的位置是n,那么如果这一次操作A[i]>=n,那么这一次操作回推的话,n一定还是这个位置,因为这次操作都是删除了后面的数;否则,就在前面插入若干段A[i]就可以了。具体细节见代码吧。

  C语言版的(这是WA的,因为没有考虑大数,说到大数我才想起来之前kuangbin的那个大数模板并不太好用。。因为他把“0”用“”代替了,下次再找一份好一点的大数模板吧。。):

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <queue>
 5 #include <vector>
 6 #include<iostream>
 7 using namespace std;
 8 const int N = 200+5;
 9 const int inf = 0x3f3f3f3f;
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 int A[10010];
13 int main()
14 {
15 
16     int t;
17     cin >> t;
18     while(t--)
19     {
20         int n;
21         scanf("%d",&n);
22         for(int i= 1;i <= n;i ++)
23             scanf("%d",&A[i]);
24         int sum= n;
25         for(int i=  n;i >= 1;i --)
26         {
27             if(A[i] >= sum)
28                 continue;
29             else
30             {
31                 /*sum = sum + (sum / A[i]-1)  * A[i] ;
32                 if(sum%A[i])
33                     sum += A[i];*/
34                 // 上面是队友写的,下面是我自己补题时写的,其实一样的- -
35                 if(sum % A[i])
36                 {
37                     sum += (sum/A[i])*A[i];
38                 }
39                 else sum += (sum/A[i]-1)*A[i];
40 
41             }
42             //cout << n - i +1<< ' ' << sum << endl;
43         }
44         cout << sum << endl;
45     }
46 
47 
48     return 0;
49 }
View Code

Java版的(AC代码):

 1 import java.math.BigInteger;
 2 import java.util.Scanner;
 3 
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7         Scanner scanner = new Scanner(System.in);
 8         Integer t = scanner.nextInt();
 9         int n;
10         int a[] = new int[10005];
11         while(t > 0){
12             n = scanner.nextInt();
13             for(int i = 1 ; i <= n ; i ++){
14                 a[i] = scanner.nextInt();
15             }
16             BigInteger sum = new BigInteger(String.valueOf(n));
17             for(int i = n ; i >= 1 ; i --){
18                 String str = String.valueOf(a[i]);
19                 BigInteger aa = new BigInteger(str);
20                 
21                 if(aa.compareTo(sum) >= 0) continue;
22                 else{
23                     BigInteger bbb = sum.divide(aa);
24                     BigInteger ccc = bbb.subtract(BigInteger.ONE);
25                     BigInteger ddd = ccc.multiply(aa);
26                     sum = sum.add(ddd);
27                     int eee = sum.remainder(aa).intValue();
28                     if(eee > 0){
29                         sum = sum.add(aa);
30                     }
31                 }
32             }
33             System.out.println(sum);
34             t --;
35         }
36     }
37 }
View Code

   接着是1002,给出n个点,问是否存在一个圆,使得至少n/3的点在这个圆上。方法是随机取3个点,由三点共圆找出圆心和半径,之后判断。关于随机的方法是否成立的问题,是这样分析的:如果存在这么一个圆,至少n/3的点在这个圆上面,那么也就是说每个点至少有1/3的概率被选到是存在与这个圆上的,那么3个点都是在这个圆上的概率是1/27,反过来,取1次这三个点至少有一个点不在这个圆上的概率是26/27,那么取n次,每次取3个点,每次这3个点都不满足三个点都在这个圆上的概率是(26/27)^n,那么至少有一次满足三个点都在圆上的概率为1-(26/27)^n,而测试数据有20组,所以这个值再来20次幂就是取n次每次随机取3个点这样来判断是否存在这么一个圆的正确率。显然,时间和正确率都和n的取值有关,经测验,n=600左右就已足够(虽然标程给的是n=800)。不管怎样,这个随机的方法真的是厉害啊- -。代码如下(标程):

 1 //shjj-Cir
 2 
 3 #include <iostream>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cstdlib>
 7 #include <cmath>
 8 #include <ctime>
 9 
10 using namespace std;
11 
12 const double eps=1e-2;
13 double X[30003],Y[30003];
14 int n,Lim;
15 double sqr(double x)
16 {
17     return x*x;
18 }
19 
20 void Cir(double ax,double ay,double bx,double by,double cx,double cy,double &x,double &y)
21 {
22      double a1=atan2(by-ay,bx-ax)+acos(-1.)/2;  
23      double a2=atan2(cy-by,cx-bx)+acos(-1.)/2;  
24      ax=(ax+bx)/2,ay=(ay+by)/2;  
25      bx=(cx+bx)/2,by=(cy+by)/2;  
26      double r=(sin(a2)*(ax-bx)+cos(a2)*(by-ay))/(sin(a1)*cos(a2)-sin(a2)*cos(a1));  
27      x=ax+r*cos(a1),y=ay+r*sin(a1);
28 }
29 
30 int main()
31 {
32     //freopen("circle.in","r",stdin);
33     //freopen("circle.out","w",stdout);
34     srand(time(0));
35     int T;scanf("%d",&T);
36     for (;T--;)
37     {
38         scanf("%d",&n);
39         for (int i=1;i<=n;i++) scanf("%lf%lf",&X[i],&Y[i]);
40         Lim=n/3;
41         bool P=0;
42         for (int ii=800;ii;ii--)
43         {
44             int t1=rand()%n+1;
45             int t2=rand()%n+1;
46             for (;t2==t1;t2=rand()%n+1);
47             int t3=rand()%n+1;
48             for (;t3==t1||t3==t2;t3=rand()%n+1);
49             double Xx,Yy,R;
50             Cir(X[t1],Y[t1],X[t2],Y[t2],X[t3],Y[t3],Xx,Yy);
51             R=sqr(X[t3]-Xx)+sqr(Y[t3]-Yy);
52             int ss=0;
53             for (int j=1;j<=n;j++)
54             {
55                 if (fabs(sqr(X[j]-Xx)+sqr(Y[j]-Yy)-R)<eps) ss++;
56                 if (ss>=Lim) break;
57             }
58             if (ss>=Lim)
59             {
60                 P=1;
61                 break;
62             }
63         }
64         if (P) puts("YOUGE");
65             else puts("NIUGE");
66     }
67 }
View Code
原文地址:https://www.cnblogs.com/zzyDS/p/5672805.html