校赛(网络赛)题解

kdl12138

2018-10-21 22:52:10

Personal

# 题解 ## A 题意就是找出所有的单身狗,然后题目保证了单身狗居多,所以我们并不用考虑现充的影响,他们投票是无效的,意味着我们只需要考虑哪些人的得票数大于一半,就可以了,等于是开一个一维数组保存,最后遍历一遍就行。oj不支持忽略行末空格,所以需要进行处理,不然会导致PE. 代码: ``` #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> using namespace std; int a[10005]; int main() { int n; while(~scanf("%d", &n)){ memset(a, 0, sizeof(a)); for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ int temp; scanf("%d", &temp); if(temp){ a[j]++; } } } int p = 1; for(int i = 1; i <= n; i++){ if(a[i] > n / 2){ if(p){ printf("%d", i); p = 0; } else{ printf(" %d", i); } } } printf("\n"); } return 0; } ``` ## E 这题主要是给了一些点,点与点之间有无向边,所以可以看作一个在t步之内我们到达其他所有点的种类数之和(有点绕)。那么我们从一个点到另一个点的种类数如果我们规定需要走t步,那么我们可以利用离散中的知识得出,将邻接矩阵的t次方计算出来,然后找到对应的位置,该位置对应的数字就是种类数。那么现在我们需要知道的是t步之内,所以我们需要将从1次方到t次方的矩阵相加起来,因为范围比较大所以我们可以利用快速幂的思想,来二分求解,那么问题我们就转化成了一个二分等比矩阵求和的问题。然后题目规定了起点,我们只需要将所得矩阵的第一行相加,最后输出答案的时候加一即可(因为可以在1处直接抓人)。总体复杂度应该是nlogn,集训队里面有两个神仙用dp+xjr优化过了这题,期待他们的题解。 代码: ``` #include <stdio.h> #include <iostream> #include <algorithm> #include <set> #include <map> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> using namespace std; const int maxn = 35; int mod = 2018, n, m; struct matrix { int mat[maxn][maxn]; void init() { memset(mat,0,sizeof(mat)); for(int i = 1; i <= maxn; i++) mat[i][i] = 1; } }a, res, res1; matrix matrixAdd(matrix x, matrix y) { matrix tmp; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){ tmp.mat[i][j] = x.mat[i][j] + y.mat[i][j]; if( tmp.mat[i][j] >= mod ) tmp.mat[i][j] %= mod; } return tmp; } matrix matrixMul(matrix x, matrix y) { matrix tmp; memset(tmp.mat,0,sizeof(tmp.mat)); for(int i = 1; i <= n; i++){ for(int k = 1; k <= n; k++){ if(x.mat[i][k] == 0) continue; for(int j = 1; j <= n; j++){ tmp.mat[i][j] += x.mat[i][k] * y.mat[k][j]; if(tmp.mat[i][j] >= mod) tmp.mat[i][j] %= mod; } } } return tmp; } matrix Mul(matrix x, int k) { matrix tmp; tmp.init(); while(k){ if(k & 1) tmp = matrixMul(tmp,x); x = matrixMul(x,x); k >>= 1; } return tmp; } matrix Sum(matrix x, int k) { if(k == 1) return x; matrix tmp; tmp.init(); tmp = matrixAdd(tmp,Mul(x,k>>1)); tmp = matrixMul(tmp,Sum(x,k>>1)); if(k&1) tmp = matrixAdd(tmp,Mul(x,k)); return tmp; } void output() { for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ if(j < n) printf("%d ",res1.mat[i][j]); else printf("%d\n",res1.mat[i][j]); } } } int main() { while(~scanf("%d %d", &n, &m)){ int t1, t2; for(int i = 1; i <= m; i++){ scanf("%d %d", &t1, &t2); a.mat[t1][t2] = a.mat[t2][t1] = 1; } for(int i = 1; i <= n; i++){ a.mat[i][i] = 1; } int t; scanf("%d", &t); res = Sum(a, t); res1 = Mul(a, t); int ans = 0; for(int i = 1; i <= n; i++){ ans += res.mat[1][i]; } ans++; ans %= 2018; printf("%d\n", ans); } return 0; } ``` ## F 这个是一个依赖背包裸题,实际上就是路径压缩的并查集+01背包,因为每一个有依赖的物品我们就可以直接看作同一个物品,所以,我们可以先利用并查集处理一下,然后保存的就是我们真实需要处理的物品,之后利用01背包求解即可。 代码: ``` #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <map> #include <iostream> using namespace std; int v[10005]; int c[10005]; int f[10005]; int a[10005]; int ansv[10005]; int ansc[10005]; int ans[10005]; int fa[10005]; int n, m, w; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void unio(int a,int b) { int f1 = find(a); int f2 = find(b); if(f1 != f2) fa[f1] = f2; } void init() { for(int i = 1; i <= n; i++) fa[i] = i; } int main() { while(~scanf("%d %d %d", &n, &m, &w)){ memset(f, 0, sizeof(f)); memset(a, 0, sizeof(a)); memset(ans, 0, sizeof(ans)); memset(ansv, 0, sizeof(ansv)); memset(ansc, 0, sizeof(ansc)); init(); for(int i = 1; i <= n; i++){ scanf("%d %d", &v[i], &c[i]); } for(int i = 0; i < m; i++){ int t, p; scanf("%d %d", &t, &p); unio(t, p); } int cnt=0; map<int,int> mp; for(int i = 1; i <= n; i++){ int res = find(fa[i]); if(!mp.count(res)) mp[res]=++cnt; ansc[mp[res]] += c[i]; ansv[mp[res]] += v[i]; } for(int i = 1;i <= cnt;i++) for(int j = w; j >= ansv[i];j--) ans[j] = max(ans[j], ans[j - ansv[i]] + ansc[i]); printf("%d\n",ans[w]); } return 0; } ``` ## G 这题本质上就是一个大模拟,看懂代码应该就能写,主要卡点在于位运算可逆,如果知道这一个,那么就可以根据所给代码进行反推,进行一次大模拟。群里之前有人问样例为什么是两个数字,这是因为给的n是2,所以会加密出两个数字。 代码: ``` #include <stdio.h> #include <string.h> unsigned int data[100005]; char ans[400005]; unsigned int solve[100005]; unsigned int crypto(unsigned int x) { return x ^ (x >> 16); } int main() { int n; int iv; while (~scanf("%d %d", &n, &iv)) { memset(data, 0, sizeof(data)); memset(ans, 0, sizeof(ans)); memset(solve, 0, sizeof(solve)); for (int i = 0; i < n; i++) { scanf("%d", &data[i]); } for (int i = n - 1; i > 0; i--) { solve[i] = crypto(data[i]); solve[i] ^= data[i - 1]; } solve[0] = crypto(data[0]); solve[0] ^= iv; for (int i = 0; i < n; i++) { for (int j = 0; j < 4; j++) { ans[i * 4 + j] = (solve[i] >> ((3 - j) * 8)) & 127; } } printf("%s\n", ans); } return 0; } ``` ## I 这题是一个dfs的基础进阶题,因为规定了走的步数,所以用bfs找到最短路径的方法就被毙了。那么我们只要一直进行dfs直到t步数的时候一直return就行,如果没有答案,那也就return。但是这题数据范围比较大,所以我们需要在刚开始和dfs的时候进行剪枝,一次是t大于所有可走点,一次是奇偶性不同(剩余时间和离终点的曼哈顿距离),还有一次是剩余时间小于离终点的曼哈顿距离,理论上剪完这三个枝,就可以通过这一题。 代码: ``` #include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> #include <cmath> #include<queue> #include<map> #include<string> #include<string.h> #include<vector> #include<stack> #include<set> using namespace std; bool vis[100][100]; bool res; int maze[100][100]; int sx = 0, sy = 0, ex = 0, ey = 0; int n, m, t; void dfs(int tx, int ty, int tm) { vis[tx][ty] = true; if (tm == t && tx == ex && ty == ey) { res = true; vis[tx][ty] = false; return; } if (tm > t || (((t - tm)&1)^((abs(ex - tx) + abs(ey - ty))&1)) || (t-tm<abs(ex - tx) + abs(ey - ty))) { vis[tx][ty] = false; return; } if (tx - 1 >= 1 && !vis[tx - 1][ty] && maze[tx - 1][ty] != 1) { dfs(tx - 1, ty, tm + 1); if (res) { vis[tx][ty] = false; return; } } if (tx + 1 <= n && !vis[tx + 1][ty] && maze[tx + 1][ty] != 1) { dfs(tx + 1, ty, tm + 1); if (res) { vis[tx][ty] = false; return; } } if (ty - 1 >= 1 && !vis[tx][ty - 1] && maze[tx][ty - 1] != 1) { dfs(tx, ty - 1, tm + 1); if (res) { vis[tx][ty] = false; return; } } if (ty + 1 <= m && !vis[tx][ty + 1] && maze[tx][ty + 1] != 1) { dfs(tx, ty + 1, tm + 1); if (res) { vis[tx][ty] = false; return; } } vis[tx][ty] = false; } int main() { while (scanf("%d%d%d", &n, &m, &t) != EOF) { if (n == 0 && m == 0 && t == 0) break; char c; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf(" %c", &c); if (c == 'X') { maze[i][j] = 1; } else if (c == 'S') { maze[i][j] = 2; sx = i; sy = j; } else if (c == 'D') { maze[i][j] = 3; ex = i; ey = j; } else if (c == '.') { maze[i][j] = 4; } } } if ((sx == 0 && sy == 0) || (ex == 0 && ey == 0) || (t > n*m)) { printf("NO\n"); continue; } res = false; memset(vis, 0, sizeof(vis)); dfs(sx, sy, 0); if (res) printf("YES\n"); else printf("NO\n"); } return 0; } ``` ## J 水题无疑,刚开始没看清楚数据范围~~眼瞎了~~,后来直接上大数板子就可以过了,这题就是一个斐波那契的问题,因为可以转化为f(n) = f(n - 1) + f(n - 2),所以就是一个斐波那契加高精的一个裸题。注意不要写成递归,如果数据再大一点就会导致爆栈。整体时间复杂度为O(n),常数主要是在实现大数上。 代码:(板子比较长) ``` #include <iostream> #include <vector> #include <string> #include <fstream> using namespace std; int n, m, k; struct Wint:vector<int> { Wint(int n=0) { push_back(n); check(); } Wint& check() { while(!empty()&&!back())pop_back(); if(empty())return *this; for(int i=1; i<size(); ++i) { (*this)[i]+=(*this)[i-1]/10; (*this)[i-1]%=10; } while(back()>=10) { push_back(back()/10); (*this)[size()-2]%=10; } return *this; } }; istream& operator>>(istream &is,Wint &n) { string s; is>>s; n.clear(); for(int i=s.size()-1; i>=0; --i)n.push_back(s[i]-'0'); return is; } ostream& operator<<(ostream &os,const Wint &n) { if(n.empty())os<<0; for(int i=n.size()-1; i>=0; --i)os<<n[i]; return os; } bool operator!=(const Wint &a,const Wint &b) { if(a.size()!=b.size())return 1; for(int i=a.size()-1; i>=0; --i) if(a[i]!=b[i])return 1; return 0; } bool operator==(const Wint &a,const Wint &b) { return !(a!=b); } bool operator<(const Wint &a,const Wint &b) { if(a.size()!=b.size())return a.size()<b.size(); for(int i=a.size()-1; i>=0; --i) if(a[i]!=b[i])return a[i]<b[i]; return 0; } bool operator>(const Wint &a,const Wint &b) { return b<a; } bool operator<=(const Wint &a,const Wint &b) { return !(a>b); } bool operator>=(const Wint &a,const Wint &b) { return !(a<b); } Wint& operator+=(Wint &a,const Wint &b) { if(a.size()<b.size())a.resize(b.size()); for(int i=0; i!=b.size(); ++i)a[i]+=b[i]; return a.check(); } Wint operator+(Wint a,const Wint &b) { return a+=b; } Wint& operator-=(Wint &a,Wint b) { if(a<b)swap(a,b); for(int i=0; i!=b.size(); a[i]-=b[i],++i) if(a[i]<b[i]) { int j=i+1; while(!a[j])++j; while(j>i) { --a[j]; a[--j]+=10; } } return a.check(); } Wint operator-(Wint a,const Wint &b) { return a-=b; } Wint operator*(const Wint &a,const Wint &b) { Wint n; n.assign(a.size()+b.size()-1,0); for(int i=0; i!=a.size(); ++i) for(int j=0; j!=b.size(); ++j) n[i+j]+=a[i]*b[j]; return n.check(); } Wint& operator*=(Wint &a,const Wint &b) { return a=a*b; } Wint divmod(Wint &a,const Wint &b) { Wint ans; for(int t=a.size()-b.size(); a>=b; --t) { Wint d; d.assign(t+1,0); d.back()=1; Wint c=b*d; while(a>=c) { a-=c; ans+=d; } } return ans; } Wint operator/(Wint a,const Wint &b) { return divmod(a,b); } Wint& operator/=(Wint &a,const Wint &b) { return a=a/b; } Wint& operator%=(Wint &a,const Wint &b) { divmod(a,b); return a; } Wint operator%(Wint a,const Wint &b) { return a%=b; } Wint f[30005]; void init() { f[0] = 0; f[1] = 1; f[2] = 2; for(int i = 3; i <= 5000; i++){ f[i] = f[i - 1] + f[i - 2]; } } int main() { int n; init(); while(cin >> n){ cout << f[n] << endl; } return 0; } ```