预估难度:
签到题:学长的温馨提示,maomeng的mimi和miaomiao,maomeng简易版消消看。
简单题:A君的音乐游戏,集训队预备队点名,学不会了,levil的三角形问题,1 ~ n的二进制表示中1的个数
较难题:A-B Problem,levil的叠数游戏,levil的外挂,levil养鱼。
难题:学长的口袋妖怪。
学长的温馨提示:if依次判断每个条件是否满足。
#include<bits/stdc++.h> using namespace std; int main() { long long n;cin >> n; if(n <= 20) printf("O(2^n)\n"); if(n <= 100) printf("O(n^3)\n"); if(n <= 1000) printf("O(n^2)\n"); if(n <= 10000) printf("O(nlogn)\n"); if(n <= 1000000) printf("O(n)\n"); printf("O(logn)\n"); }
maomeng的mimi和miaomiao:
枚举一个人走了步数,然后计算另外一个人走几步能到最小距离。
我们假设枚举的那个人走的步数剩下的距离 = dis。
然后另外一个人一步能走的距离 = y。
那么他们能走的最近的距离就为 dis % y,当dis % y != 0时。
如果dis % y == 0,那么两个人会重合,那么第二个人就要少走一步,这个时候最小距离 = y。
最后取最小值即可。
#include<bits/stdc++.h> using namespace std; int main() { int n,x,y;scanf("%d %d %d",&n,&x,&y); int ans = 105; for(int i = 0;i <= n / x;++i) { int dis = n - i * x; if(dis % y == 0) ans = min(ans,y); else ans = min(ans,dis % y); } printf("%d\n",ans); }
maomeng简易版消消看:
暴力枚举两个相邻的点交换,然后判断当前是否存在一行或者一列都相同即可。
#include<bits/stdc++.h> using namespace std; int a[5][5]; bool check() { for(int i = 1;i <= 3;++i) { if(a[i][1] == a[i][2] && a[i][2] == a[i][3]) return true; if(a[1][i] == a[2][i] && a[2][i] == a[3][i]) return true; } return false; } int main() { for(int i = 1;i <= 3;++i) { for(int j = 1;j <= 3;++j) { scanf("%d",&a[i][j]); } } int f = 0; for(int i = 1;i <= 3;++i) { for(int j = 1;j <= 3;++j) { if(j != 3) { int t = a[i][j]; a[i][j] = a[i][j + 1]; a[i][j + 1] = t; if(check()) f = 1; t = a[i][j]; a[i][j] = a[i][j + 1]; a[i][j + 1] = t; } if(i != 3) { int t = a[i][j]; a[i][j] = a[i + 1][j]; a[i + 1][j] = t; if(check()) f = 1; t = a[i][j]; a[i][j] = a[i + 1][j]; a[i + 1][j] = t; } } } if(f) printf("Yes\n"); else printf("No\n"); }
集训队预备队点名:给后面输入的k个人名字后面拼接上21,这样就可以直接字符串比较了。
#include <bits/stdc++.h> using namespace std; char a[15][15][30],s[35][30]; bool vis[35]; int main() { int n,m;scanf("%d %d",&n,&m); for(int i = 0;i < n;++i) { for(int j = 0;j < m;++j) scanf("%s",a[i][j]); } int k;scanf("%d",&k); for(int i = 0;i < k;++i) { scanf("%s",s[i]); int len = strlen(s[i]); s[i][len] = '2',s[i][len + 1] = '1',s[i][len + 2] = '\0'; } for(int i = 0;i < n;++i) { for(int j = 0;j < m;++j) { for(int st = 0;st < k;++st) { if(strcmp(s[st],a[i][j]) == 0) vis[st] = 1; } } } int flag = 0; for(int i = 0;i < k;++i) { if(vis[i] == 0) { flag = 1; int len = strlen(s[i]); for(int j = 0;j < len - 2;++j) printf("%c",s[i][j]); printf("\n"); } } if(flag == 0) printf("Study hard!\n"); return 0; }
学不会了:
先暴力枚举第几列,然后一行行枚举计算每个字符出现的次数即可。
#include<bits/stdc++.h> using namespace std; int n,m,k,cnt[30]; char s[5005][5005]; bool vis[5005]; int main() { scanf("%d %d %d",&n,&m,&k); for(int i = 0;i < n;++i) scanf("%s",s[i]); for(int j = 0;j < m;++j) { memset(cnt,0,sizeof(cnt)); for(int i = 0;i < n;++i) { int x = s[i][j] - 'a'; cnt[x]++; if(cnt[x] >= k) vis[j] = 1; } } for(int j = 0;j < m;++j) { if(vis[j]) printf("Yes\n"); else printf("No\n"); } }
levil的三角形问题:
n ^ 3暴力枚举三个点,再判断是否能形成三角形。
判断方法1:矢量叉积判断:
#include<bits/stdc++.h> using namespace std; int x[305],y[305]; bool check(int i,int j,int k) { if (( y[i] - y[j])*(x[i] - x[k]) == (y[i] - y[k])*(x[i] - x[j]) ) return false; else return true; } int main() { int n;cin >> n; for(int i = 1;i <= n;++i) cin >> x[i] >> y[i]; int ans = 0; for(int i = 1;i <= n;++i) { for(int j = i + 1;j <= n;++j) { for(int k = j + 1;k <= n;++k) { if(check(i,j,k)) ans++; } } } printf("%d\n",ans); return 0; }
判断方法2:斜率判断:
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; int n; int x[330],y[330]; int check(ll x1,ll y1,ll x2,ll y2){ if(x1==0&&x2==0)return 0; else if(x1==0)return 1; else if(x2==0)return 1; return fabs(1.0*y1/x1-1.0*y2/x2)>=0.00001; } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&x[i],&y[i]); } long long res=0; for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ for(int k=j+1;k<=n;k++){ if(check(x[i]-x[j],y[i]-y[j],x[i]-x[k],y[i]-y[k]))res++; } } } cout<<res<<endl; return 0; }
1 ~ n的二进制表示中1的个数:
做法1:找规律。
#include <bits/stdc++.h> using namespace std; typedef long long LL; void solve(LL n) { n++; LL p=1,ans=0; while(p<=n) { p<<=1; LL num=n/p,re=n%p; ans+=num*p/2; ans+=max((LL)0,re-p/2); } printf("%lld\n",ans); } int main() { LL t; while(~scanf("%lld",&t)) { solve(t); } }
做法2:dp
#include <bits/stdc++.h> using namespace std; typedef long long LL; int a[35]; LL dp[35],pw[35];//dp[i] - 长度为i的串能形成的数量,pw[i] - 2 ^ i void init() { dp[1] = 1,pw[0] = 1,pw[1] = 2; for(int i = 2;i < 35;++i) { dp[i] = 2 * dp[i - 1] + pw[i - 1]; pw[i] = pw[i - 1] * 2; } } int main() { init(); int n; while(~scanf("%d",&n)) { int len = 0; while(n) { a[++len] = n % 2; n /= 2; } LL ans = 0; int pre = 0; for(int i = len;i >= 1;--i) { if(a[i] == 0) continue; ans += dp[i - 1] + pw[i - 1] * pre; pre++; } ans += pre;//全 - 1 printf("%lld\n",ans); } return 0; }
A-B Problem : 进制转换
先将a,b都转化为k进制下的数,然后从低位开始依次判断是否需要借位。
#include <bits/stdc++.h> using namespace std; typedef unsigned long long LL; int a[100],b[100]; int main() { LL a1,b1;scanf("%llu %llu",&a1,&b1); int k;scanf("%d",&k); int tot1 = 0,tot2 = 0; while(a1) { a[tot1++] = a1 % k; a1 /= k; } while(b1) { b[tot2++] = b1 % k; b1 /= k; } int cnt = 0,pos; for(int i = 0;i < tot2;++i) { if(a[i] < b[i]) { for(int j = i + 1;;++j) { if(a[j] != 0) { a[j]--; pos = j; break; } } cnt += pos - i; for(int j = i + 1;j < pos;++j) a[j] = k - 1; } } if(cnt == 0) printf("\"XHY is so much happy!\""); else printf("%d\n",cnt); return 0; }
A君的音乐游戏:
考虑贪心地想,分数低的操作排在前面,分数高的操作排在后面,能让最大值更大。
分数高的操作排在前面,分数低的操作排在后面,能让最大值更小。
然后对于前后半部分的计算都是一个等差数列的求和。
#include<bits/stdc++.h> using namespace std; typedef long long LL; int main() { int n;scanf("%d",&n); n++; LL mi = 0,mx = 0; while(n--) { int g,p;scanf("%d %d",&g,&p); LL sum = (LL)g * 1000 + (LL)(1 + g - 1) * (g - 1) / 2; sum += (LL)p * 2000 + (LL)(g + g + p - 1) * p / 2 * 2; mx += sum; sum = (LL)p * 2000 + (LL)(1 + p - 1) * (p - 1) / 2 * 2; sum += (LL)g * 1000 + (LL)(p + p + g - 1) * g / 2; mi += sum; } printf("%lld %lld\n",mi,mx); }
levil的叠数游戏:简单深搜
#include<bits/stdc++.h> using namespace std; typedef long long LL; int n,x,a[25],ans; void dfs(int len,LL sum,int cnt1,int cnt2) { if(sum == x) { if(cnt1 != 0 && cnt1 % 2 == 0) return ; if(cnt2 != 0 && cnt2 % 2 != 0) return ; ans++; return ; } if(sum > x || len > n) return ; if(a[len] % 2 == 0) dfs(len + 1,sum + a[len],cnt1,cnt2 + 1); else dfs(len + 1,sum + a[len],cnt1 + 1,cnt2); dfs(len + 1,sum,cnt1,cnt2); } int main() { scanf("%d %d",&n,&x); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); dfs(1,0,0,0); printf("%d\n",ans); return 0; }
levil的外挂:两次bfs
很显然最短距离就是levil -> 小龙的最短距离 + 小龙到showmaker的最短距离。
两次bfs求出这两个最短距离即可。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 55; bool vis[N][N],vt[N][N]; int dir[8][2] = {1,0,-1,0,0,1,0,-1,1,1,1,-1,-1,1,-1,-1},n,m; struct Node{int x,y,z;}; int bfs(Node st,Node ed) { queue<Node> Q; memset(vt,0,sizeof(vt)); vt[st.x][st.y] = 1; Q.push(Node{st.x,st.y,0}); while(!Q.empty()) { Node q = Q.front(); Q.pop(); if(q.x == ed.x && q.y == ed.y) return q.z; for(int i = 0;i < 8;++i) { int px = q.x + dir[i][0],py = q.y + dir[i][1]; if(px >= 1 && px <= n && py >= 1 && py <= n && !vt[px][py] && !vis[px][py]) { vt[px][py] = 1; Q.push(Node{px,py,q.z + 1}); } } } return -1; } int main() { int ca;scanf("%d",&ca); while(ca--) { memset(vis,0,sizeof(vis)); scanf("%d %d\n",&n,&m); while(m--) { int x,y;scanf("%d %d",&x,&y); vis[x][y] = 1; } int x0,y0,x1,y1,x2,y2; scanf("%d %d %d %d %d %d",&x0,&y0,&x1,&y1,&x2,&y2); int d1 = bfs(Node{x0,y0,0},Node{x1,y1,0}); int d2 = bfs(Node{x1,y1,0},Node{x2,y2,0}); printf("we are champion!\n"); if(d1 == -1 || d2 == -1) printf("-1\n"); else printf("%d\n",d1 + d2); } return 0; }
levil养鱼:很显然他们的之间的差值是呈现单调递减的,所以可以二分差值。
当差值 >= 0时,说明可能有更优取法。
可以预处理一下两个的前缀和。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e6; LL tmp,pre[N + 5]; int x,y; LL check(int i) { LL val1 = x + 1LL * (1 + i) * i / 2; tmp = val1; LL val2 = y + pre[i]; LL dis = val1 - val2; return dis; } int main() { for(int i = 1;i < N;++i) pre[i] = pre[i - 1] + 2 * i - 1; int ca;cin >> ca; while(ca--) { scanf("%d %d",&x,&y); int L = 0,r = 2e3 + 1; LL ans = x; while(L <= r) { int mid = (L + r) >> 1; LL d = check(mid); if(d >= 0) { ans = max(ans,tmp); L = mid + 1; } else r = mid - 1; } printf("%lld\n",ans); } return 0; }
因为数据造的有点弱,比赛的时候被大家暴力水过去了....
学长的口袋妖怪:状压dp
dp[i][j] - 表示状态集为i情况下,当前站在j点的方案数。
预处理一下子集点和非子集点来加速。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; #define dbg(ax) cout << "now is " << ax << endl const int N = 55; int dir[4][2] = {-1,0,0,-1,-1,-1,-1,1},mp[20][20],tim = -1,G[20],dp[1 << 20][20]; int vec[1 << 20][21],rvec[1 << 20][21]; bool vis[20][20]; bool check(int x,int y) { if(!vis[x][y] && x >= 0 && x <= 4 && y >= 0 && y <= 4) return true; else return false; } void add(int x,int y) { G[x] |= (1 << y); G[y] |= (1 << x); } void init(int up) { for(int i = 0;i < (1 << 20);++i) { for(int j = 0;j < up;++j) { if((i >> j) & 1) { ++vec[i][0]; vec[i][vec[i][0]] = j; } else { ++rvec[i][0]; rvec[i][rvec[i][0]] = j; } } } } void solve() { for(int i = 1;i <= 5;++i) { int x,y;cin >> x >> y; vis[x - 1][y - 1] = 1; } for(int i = 0;i < 5;++i) { for(int j = 0;j < 5;++j) { if(vis[i][j]) continue; mp[i][j] = ++tim; if(i == 0 || i == 4 || j == 0 || j == 4) dp[1 << tim][tim] = 1; for(int k = 0;k < 4;++k) { int px = i + dir[k][0],py = j + dir[k][1]; if(check(px,py)) add(tim,mp[px][py]); } } } init(tim + 1); for(int i = 1;i < (1 << 20);++i) { int up1 = vec[i][0],up2 = rvec[i][0]; for(int j = 1;j <= up1;++j) { int v = vec[i][j]; for(int k = 1;k <= up2;++k) { int vv = rvec[i][k]; if((G[v] >> vv) & 1) dp[i | (1 << vv)][vv] += dp[i][v]; } } } LL ans = 0; for(int i = 0;i < 20;++i) ans += dp[(1 << 20) - 1][i]; printf("%lld\n",ans); } int main() { solve(); return 0; }
也可以用状压加上记忆化搜索的做法过。