江西理工摸底测试
A题:water:
一开始写的时候,写超时了:一是有的条件没加上去,二是没有进行优化。
后来用了set将没有装满的瓶子的下标放进去,并进行排序,然后二分查找找后面比他大的下标。
我觉得这样的思路非常的ok,应该没毛病,但是就是老wa,原来在后面的循环没有赋初值。
下面是AC代码
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <set> 5 6 using namespace std; 7 const int ma = 1e5 + 10; 8 int t,n,m; 9 set<int> s; 10 struct node 11 { 12 int x,y; 13 }num[ma]; 14 15 int main() 16 { 17 scanf("%d",&t); 18 while(t--) 19 { 20 s.clear(); 21 scanf("%d%d",&n,&m); 22 for(int i = 1;i <= n;i++) 23 scanf("%d",&num[i].x),s.insert(i),num[i].y=0; 24 int a,b,c; 25 while(m--) 26 { 27 scanf("%d%d",&a,&b); 28 c = min(num[a].x - num[a].y,b); 29 num[a].y += c; 30 b -= c; 31 if(num[a].x == num[a].y) 32 s.erase(a); 33 set<int>::iterator it; 34 while(b > 0) 35 { 36 it = s.lower_bound(a); 37 if(it == s.end()) 38 break; 39 a = *it; 40 c = min(num[a].x - num[a].y,b); 41 num[a].y += c; 42 if(num[a].x == num[a].y) 43 s.erase(a); 44 b -= c; 45 } 46 } 47 for(int i = 1;i <= n;i++) 48 printf("%d%c",num[i].y," \n"[i==n]); 49 } 50 return 0; 51 }
后面发现还可以这样写:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <set> 5 6 using namespace std; 7 const int ma = 1e5 + 10; 8 int t,n,m; 9 set<int> s; 10 struct node 11 { 12 int x,y; 13 }num[ma]; 14 15 int main() 16 { 17 scanf("%d",&t); 18 while(t--) 19 { 20 s.clear(); 21 scanf("%d%d",&n,&m); 22 for(int i = 1;i <= n;i++) 23 scanf("%d",&num[i].x),s.insert(i),num[i].y=0; 24 int a,b,c; 25 while(m--) 26 { 27 scanf("%d%d",&a,&b); 28 c = min(num[a].x - num[a].y,b); 29 num[a].y += c; 30 b -= c; 31 if(num[a].x == num[a].y) 32 s.erase(a); 33 set<int>::iterator it; 34 while(b > 0) 35 { 36 it = s.lower_bound(a); 37 if(it == s.end()) 38 break; 39 a = *it; 40 c = min(num[a].x - num[a].y,b); 41 num[a].y += c; 42 if(num[a].x == num[a].y) 43 s.erase(a); 44 b -= c; 45 } 46 } 47 for(int i = 1;i <= n;i++) 48 printf("%d%c",num[i].y," \n"[i==n]); 49 } 50 return 0; 51 }
B题:题意是讲一个数分成三个素数的和,问有多少种分法?
首先肯定是先将n内的素数判断出来,然后进行有技巧的暴力,划重点是有技巧的暴力。
我天真的认为可以将出现的三个素数记录下来,然后它们再次出现的时候,就跳过,然而还是不行。
比赛完之后知道了怎么更好的解决这个问题,控制下标再加上一点限制就可以了。
下面是AC代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <bits/stdc++.h> 6 7 using namespace std; 8 const int ma = 4e4 + 10; 9 int n,m,cnt,prime[ma]; 10 set<int> pr; 11 bool vis[ma]; 12 13 void prim(int maxx) 14 { 15 for(int i = 2; i <= maxx; i++) 16 { 17 if(!vis[i]) 18 prime[++cnt] = i; 19 for(int j = 1; j <= cnt && i*prime[j] <= maxx; j++) 20 { 21 vis[i*prime[j]] = 1; 22 if(i % prime[j] == 0) 23 break; 24 } 25 } 26 } 27 struct node 28 { 29 int x,y,z; 30 }; 31 32 int main() 33 { 34 int t; 35 scanf("%d",&t); 36 prim(40000); 37 node a; 38 int b,ans; 39 while(t--) 40 { 41 scanf("%d",&n); 42 ans = 0; 43 for(int i = 1; prime[i] <= n; i++) 44 { 45 for(int j = i; prime[j] <= n; j++) 46 { 47 b = prime[i] + prime[j]; 48 if(b >= n) 49 break; 50 if(!vis[n - b]) 51 { 52 if(n - b >= prime[j]) 53 ans++; 54 } 55 } 56 } 57 printf("%d",ans); 58 if(t != 0) 59 printf("\n"); 60 } 61 return 0; 62 }
C题:有n个顶点,每个顶点有自己的坐标(Xi,Yi,Zi),现在想把这n个顶点连接起来,已知连接两个顶点u和v的花费是MIN(|Xu−Xv|,|Yu−Yv|,|Zu−Zv|)。现在,请花费最小的代价把这些点连接起来。
看到题目就知道要用最小生成树来写,但是我们需要有边,当然这里也有边,但问题是这里用太多的边了,来,让我们来算一下总共有多少条边,先来看一下x坐标,假设有n个点所以x1x2x3···xn
x1 与剩下的点都可以连成边所以就有n-1条边,所以一个点就有用n-1条边,n个点就有n*(n-1),这只是以x而建立的边,所以一共就有3*n(n-1),少一点也是3n2 边,然而n的范围是2e5,所以肯定不能开那么大的数组,那就意味着我们必须要把一些不必要的边给去掉,但是把哪些边去掉呢,这个时候我们可以联想到克鲁斯卡尔算法,克鲁斯卡尔算法是让一个个还未加入团的点进团,下次遇到了在同一个团的点就不加进来,所以我们就可以根据x,y,z来排序,将相邻的点连成边,然后放进一个结构体的数组里,再将它们一起排序(从小到大的方式),因为之前将短的连在了一起,所以后面更长的边就没有用了。所以就可以将x,y,z排序,再用克鲁斯卡尔算法。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 6 using namespace std; 7 #define pai pair<int,int> 8 const int ma = 2e5 + 10; 9 int x[ma],y[ma],z[ma],father[ma]; 10 pai pp[ma]; 11 int n; 12 struct edge 13 { 14 int from,to,val; 15 } num[4*ma]; 16 17 bool cmp(pai a,pai b) 18 { 19 return a.first < b.first; 20 } 21 bool cmp1(edge a,edge b) 22 { 23 return a.val < b.val; 24 } 25 int find_f(int a) 26 { 27 return a == father[a] ? a : father[a] = find_f(father[a]); 28 } 29 30 int main() 31 { 32 scanf("%d",&n); 33 for(int i = 1; i <= n; i++) 34 { 35 scanf("%d%d%d",&x[i],&y[i],&z[i]),father[i] = i; 36 } 37 for(int i = 1; i <= n; i++) 38 pp[i].first = x[i],pp[i].second = i; 39 sort(pp+1,pp+1+n,cmp); 40 int cnt = 0; 41 for(int i = 1; i < n; i++) 42 { 43 num[++cnt].from = pp[i].second; 44 num[cnt].to = pp[i+1].second; 45 num[cnt].val = abs(pp[i+1].first - pp[i].first); 46 } 47 for(int i = 1; i <= n; i++) 48 pp[i].first = y[i],pp[i].second = i; 49 sort(pp+1,pp+1+n,cmp); 50 for(int i = 1; i < n; i++) 51 { 52 num[++cnt].from = pp[i].second; 53 num[cnt].to = pp[i+1].second; 54 num[cnt].val = abs(pp[i+1].first - pp[i].first); 55 } 56 for(int i = 1; i <= n; i++) 57 pp[i].first = z[i],pp[i].second = i; 58 sort(pp+1,pp+n+1,cmp); 59 for(int i = 1; i < n; i++) 60 { 61 num[++cnt].from = pp[i].second; 62 num[cnt].to = pp[i+1].second; 63 num[cnt].val = abs(pp[i+1].first - pp[i].first); 64 } 65 sort(num+1,num+1+cnt,cmp1); 66 int nu = 1,fa,fb; 67 int ans = 0; 68 // for(int i = 1; i <= cnt; i++) 69 // cout<<num[i].from<<" "<<num[i].to<<" "<<num[i].val<<endl; 70 for(int i = 1; i <= cnt && nu <= n -1; i++) 71 { 72 fa = find_f(num[i].from),fb = find_f(num[i].to); 73 //cout<<fa<<" "<<fb<<endl; 74 if(fa != fb) 75 { 76 father[fa] = fb,ans += num[i].val,nu++; 77 } 78 } 79 cout<<ans<<endl; 80 return 0; 81 }
D题还没补;
E题是给出一个整数n,将其分解为若干个正整数之和,使得这些正整数的乘积x最大:
只要尽可能的分成3,如果不能分成3就分成2;
然后要扩欧求逆元就好了
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 const int ma = 100; 6 typedef long long ll; 7 ll x,y,num[ma]; 8 int n; 9 10 void exgcd(ll a,ll b) 11 { 12 if(!b) 13 { 14 x = 1,y = 0; 15 return ; 16 } 17 exgcd(b,a%b); 18 ll temp = x; 19 x = y; 20 y = temp - a / b * y; 21 } 22 int main() 23 { 24 scanf("%d",&n); 25 ll k = 1; 26 for(int i = 5;i <= 66;i += 3) 27 { 28 num[i] = 6 * k; 29 num[i+1] = 9 * k; 30 num[i+2] = 12 * k; 31 k *= 3; 32 } 33 exgcd(n,num[n]); 34 while(x < 0) 35 x += num[n]; 36 x %= num[n]; 37 cout<<x; 38 return 0; 39 }
F题是一个线段树的题直接看代码
1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 const int ma = 1e5 + 10; 6 int tree[4*ma],lazy[4*ma]; 7 int n,m,ans; 8 9 void pushdown(int rt,int l,int r) 10 { 11 lazy[2*rt] += lazy[rt]; 12 lazy[2*rt+1] += lazy[rt]; 13 int mid = (l + r)/2; 14 tree[2*rt] += lazy[rt]*(mid - l + 1); 15 tree[2*rt+1] +=lazy[rt]*(r-mid); 16 tree[2*rt] %= 2; 17 tree[2*rt+1] %= 2; 18 lazy[rt] = 0; 19 } 20 void update(int rt,int l,int r,int L,int R) 21 { 22 if(L <= l && r <= R) 23 { 24 lazy[rt] += 1; 25 tree[rt] +=(r - l + 1); 26 tree[rt] %= 2; 27 return ; 28 } 29 if(lazy[rt]) 30 pushdown(rt,l,r); 31 int mid = (l+r)/2; 32 if(L <= mid) 33 update(2*rt,l,mid,L,R); 34 if(R > mid) 35 update(2*rt+1,mid+1,r,L,R); 36 tree[rt] = tree[2*rt]+tree[2*rt+1]; 37 tree[rt] %= 2; 38 } 39 void query(int rt,int l,int r,int x) 40 { 41 if(l == r && l == x) 42 { 43 ans = tree[rt]; 44 return ; 45 } 46 int mid = (l + r) / 2; 47 if(lazy[rt]) 48 pushdown(rt,l,r); 49 if(x <= mid) 50 query(2*rt,l,mid,x); 51 else 52 query(2*rt+1,mid+1,r,x); 53 } 54 int main() 55 { 56 scanf("%d%d",&n,&m); 57 int a,b,c; 58 while(m--) 59 { 60 scanf("%d%d",&a,&b); 61 if(a == 1) 62 { 63 scanf("%d",&c); 64 update(1,1,n,b,c); 65 } 66 else 67 { 68 ans = 0; 69 query(1,1,n,b); 70 printf("%d\n",ans); 71 } 72 } 73 return 0; 74 }
G题,在经过九九八十一难之后,现在终于把g题给补完了,之前还差几个妖怪没打完。
首先这题的题意是有n个人,现在有一个聚会,每个人都可以选择参加或者不参加。而参加的人中每个人要么只去送礼物,要么只接受礼物。不存在全部都接受礼物或者全部都送礼物的情况(这样要么没人送礼物,要么没人接受礼物了)。问有多少中情况?一看完我就感觉我的数学肯定没怎么学好,一看感觉没思路。然后仔细想想,所有的人都有可能会参加聚会。在参加聚会的人里,不能让所有的人只收礼物(或者只送礼物),那肯定是啊如果所有的人都只收礼物那哪个送礼物(所有人都送礼物,哪个人来收礼物?),所以至少要有两个人要去参加聚会而且一个人送礼物,一个人收礼物。所以就可以有2,3,4,···,n个人参加聚会,选两个人的时候就是从n个人里选两个人也就是组合排序。我们假设选了x个人去参加聚会,然后每个人都有两种选择,一是收礼物,而是送礼物,所以这样的话,就有2的x次方种可能了,但是还得要减掉两种(第一种就是所有的人(这里指的是参加聚会的人)都收礼物,第二种是所有的人都送礼物)。所以解决这个问题的方法就已经知道了,就是怎么将它实现。我们都知道怎么求组合排列。但是不能这么求,这里就必须用逆元来解决了,而且必须用递推的那种。因为你会发现当分母的阶乘都是小于n的阶乘的,而且它们每次会出现两次,所以用递推的逆元。那么怎么递推呢?我们可以先求出n的阶乘的逆元,然后求n-1阶乘的逆元,到了这里你就会发现(n-1)!*n(n)-1≡1(mod p),在这里我们就会发现inv[i] = (i+1)*inv[i+1];可以自己动手推一下。然后我们的代码就出来了,不过还是要注意一下取模。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 5 using namespace std; 6 typedef long long ll; 7 const int mod = 1e9 + 7; 8 const int ma = 1e5 + 10; 9 ll inv[ma]; 10 ll x,y; 11 12 void exgcd(ll a,ll b) 13 { 14 if(!b) 15 { 16 x = 1,y = 0; 17 return ; 18 } 19 exgcd(b,a%b); 20 ll tx = x; 21 x = y; 22 y = tx - a/b*y; 23 } 24 ll pow_ll(ll a, ll y) 25 { 26 ll ans = 1; 27 while(y) 28 { 29 if(y&1) 30 ans = ans*a%mod; 31 a = a*a%mod; 32 y >>= 1; 33 } 34 return ans; 35 } 36 int main() 37 { 38 int n; 39 scanf("%d",&n); 40 ll ji = 1; 41 for(int i = 1;i <= n;i++) 42 ji =ji * i % mod; 43 exgcd(ji,mod); 44 x %= mod; 45 if(x < 0) 46 x += mod; 47 x %= mod; 48 inv[n] = x; 49 for(int i = n-1;i >= 0;i--) 50 inv[i] = (i+1)*inv[i+1]%mod; 51 ll a,b,ans = 0; 52 for(int i = 2;i <= n;i++) 53 { 54 a = pow_ll(2,i) - 2; 55 b = ji*inv[i]%mod*inv[n-i]%mod; 56 ans = (ans%mod + a*b%mod)%mod; 57 } 58 cout<<ans; 59 return 0; 60 }
H题:这题我很尴尬,竟然用错的方法过了,还是第一个过的······,我觉得脑子有坑才会那样写。
它要跑三遍的bfs第一遍跑第一个地图求出它的最短路径,第二遍跑第二个图也求最短的路径,看是否相等,相等的话就跑第三遍第一个和第二图结合的图看它的最短路径是不是一样,一样就输出YES。
看代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <queue> 6 7 using namespace std; 8 const int ma = 5e2 + 10; 9 char s1[ma][ma],ss[ma][ma]; 10 char s2[ma][ma]; 11 bool vis[ma][ma]; 12 int fx[4] = {0,1,0,-1}; 13 int fy[4] = {1,0,-1,0}; 14 int n,m,f; 15 struct node 16 { 17 int x,y; 18 int val; 19 }; 20 21 int bfs(char s[ma][ma]) 22 { 23 node a,b; 24 queue<node> q; 25 vis[0][0] = 1; 26 for(int i = 0; i < n; i++) 27 for(int j = 0; j < m; j++) 28 vis[i][j] = 0; 29 a.x = 0,a.y = 0,a.val = 0; 30 q.push(a); 31 while(!q.empty()) 32 { 33 a = q.front(); 34 q.pop(); 35 int xx,yy; 36 for(int i = 0; i < 4; i++) 37 { 38 xx = a.x + fx[i]; 39 yy = a.y + fy[i]; 40 if(xx == n - 1 && yy == m-1) 41 { 42 f++; 43 return a.val + 1; 44 } 45 if(xx >= 0 && xx < n && yy >= 0&& yy < m && s[xx][yy] != '#' && !vis[xx][yy]) 46 { 47 vis[xx][yy] = 1; 48 b.x = xx,b.y = yy,b.val = a.val + 1; 49 q.push(b); 50 } 51 } 52 } 53 return 0; 54 } 55 int main() 56 { 57 scanf("%d%d",&n,&m); 58 for(int i = 0; i < n; i++) 59 scanf(" %s",s1+i); 60 for(int i = 0; i < n; i++) 61 scanf(" %s",ss+i); 62 for(int i = 0; i < n; i++) 63 for(int j = 0; j < m; j++) 64 { 65 if(s1[i][j] == '#' || ss[i][j] == '#') 66 s2[i][j] = '#'; 67 else 68 s2[i][j] = '.'; 69 } 70 vis[0][0] = 1; 71 int len = bfs(s1),len1 = bfs(ss),len2 = bfs(s2); 72 if(n == m && n == 1) 73 { 74 bool ff = 0; 75 for(int i = 0; i < n; i++) 76 for(int j = 0; j < m; j++) 77 if(s1[i][j] != ss[i][j]) 78 ff = 1; 79 if(ff) 80 len = 2; 81 else 82 f = 3; 83 } 84 if(f == 3 && len == len1 && len1 == len2) 85 printf("YES"); 86 else 87 printf("NO"); 88 return 0; 89 }
最一道题
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <bits/stdc++.h> 6 7 using namespace std; 8 const int ma = 1e5 + 10; 9 char s[ma]; 10 int n; 11 12 int main() 13 { 14 scanf("%s",&s); 15 int len = strlen(s); 16 len--; 17 for(int i = 0,j = len;i < j;i++,j--) 18 { 19 if(s[i] < s[j]) 20 s[j] = s[i]; 21 else 22 s[i] = s[j]; 23 } 24 cout<<s; 25 return 0; 26 }