您的位置:首页 > 理论基础 > 计算机网络

wut2018校赛网络赛部分题解

2018-11-06 14:18 344 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/u013583572/article/details/83786400

wut2018校赛网络赛部分题解

文章目录

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: