先讲1007,是一个数位dp,询问一个区间内,各位数的和是一个素数的数字的个数。其实我并不会数位dp,这题直接套用了上次多校lyf队长的dp代码,改了点返回参数没想到直接AC了。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
从这个来看,看来以后对于各个位数上的和能够满足某一条件的数位dp,都可以直接套用这个模板了。顺便给下上次那个数位dp的AC代码。那题的题意是求区间上满足各位数上的和能被这个数整除的数的个数。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
1004,是一个模拟题,只要搞懂了题意就行,还好队友玩过了炉石传说,所以这题顺理成章的很快就1A了。要注意的是,mana值是没用的,有护甲的时候先扣护甲,再扣血;血有上限为30,护甲没有,在你我这样的一个大回合里,每个回合扣血数加1。具体见代码(现场上交的,没做过修改):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
1008是一个按照税法缴税的水题,但是当时题目数据给错了,所以WA倒一片。有意思的是我当时去百度了一下,找到了缴税百分比的表格,却没有发现最后一个数据是不同的!!- -,这题也真是有趣啊。。代码如下(大力学长的代码):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
1006是一个很烦的题目,题意是给出若干个点,问存在多少个矩形,它们的边都是水平或者垂直的,且四个角上都包含了点。思路是,先想办法存下离散化后的图,然后对每一列,往下推m-1行,分别找这两行往后推m-1列的纵坐标的值是否相等,若相等,则个数加1。具体见代码(有注释):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
这题是队友做出来的,这里放一下他的源代码(我们的二分写法不太一样- -,队友的数据结构真心强。躺了。。):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 */
然后是1003,也是队友的奥义思路,然后结合了大力的大数java搞出来的。这题的题意是,给出n个操作,对自然数序列(1,2,3,4...)每个操作保留A[i]个数,删除A[i]个数。问n个操作以后剩下的第n个数是多少。思路是,反推,比方说当前这个数的位置是n,那么如果这一次操作A[i]>=n,那么这一次操作回推的话,n一定还是这个位置,因为这次操作都是删除了后面的数;否则,就在前面插入若干段A[i]就可以了。具体细节见代码吧。
C语言版的(这是WA的,因为没有考虑大数,说到大数我才想起来之前kuangbin的那个大数模板并不太好用。。因为他把“0”用“”代替了,下次再找一份好一点的大数模板吧。。):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
Java版的(AC代码):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
接着是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)。不管怎样,这个随机的方法真的是厉害啊- -。代码如下(标程):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }