哈尔滨理工大学第12届程序设计竞赛(同步赛)错题笔记

官网链接

C 迷宫

题目描述
小王最近迷上了一款走迷宫游戏,这款走迷宫游戏在一个三维空间中运行,玩家在迷宫中可以通过操作手柄,每次进行上、下、左、右、前、后6个方向的移动。游戏进行过程中会出现以下两种操作中的一种:

1 x y z在迷宫的位置C(x,y,z)开放一个新的出口(原有的出口依然可用)。

2 x y z玩家出现在这个迷宫的位置Q(x,y,z)处。

对于每次操作2,玩家需要通过操作最少次数的手柄去到达一个出口,离开迷宫,输出此时的最少操作次数。

(保证第一次操作一定是操作1,也就是保证一定有出口)

输入描述:
给定一个t,代表t组测试数据。(1<=t<=12)

每组测试数据在一行给定一个n,m,h,q。(1<=n * m * h<=1e5,∑n * m * h<=5e5,1<=q<=1e5)

接下来q行,每行给定op,x,y,z。(1<=op<=2,1<=x<=n,1<=y<=m,1<=z<=h)

输出描述:
每组测试数据,针对每个操作2,输出最少操作手柄的次数。
示例一:
输入:
1
5 5 5 5
1 3 3 3
2 3 3 2
1 2 2 2
2 1 1 1
2 5 4 3
输出:
1
3
3
方法一(正解):

#include<bits/stdc++.h>

using namespace std;

#define endl '\n'//用了这个替换,时间从400多ms提升到了100多ms!
#define please return
#define ac 0

typedef struct Node
{
    int x,y,z;
} node;

void solve()
{
    int n,m,h,Q;
    cin >> n >> m >> h >> Q;

    int dx[] = {-1,0,1,0,0,0},dy[] = {0,1,0,-1,0,0},dz[] = {0,0,0,0,1,-1};

    // int dist[n + 5][m + 5][h + 5];//数组开到局部必须记得都初始化!
    // memset(dist,0x3f,sizeof(dist));
    vector<vector<vector<int>>> dist(n + 5,vector<vector<int>>(m + 5,vector<int>(h + 5)));//了解一下这种用法
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            for(int k = 1;k <= h;k++)
                dist[i][j][k] = 0x3f3f3f3f;
                
    queue<Node> q;

    auto check = [&](Node a)->bool{
        int x = a.x,y = a.y,z = a.z;
        if(x > n || x < 1 || y > m || y < 1 || z > h || z < 1)  return true;
        else    return false;
    };
    
    while (Q--)
    {
        int op,x,y,z;
        cin >> op >> x >> y >> z;
        if(op == 1)
        {
            dist[x][y][z] = 0;
            q.push({x,y,z});
        }
        else
        {
            while (!q.empty())
            {
                auto t = q.front();
                q.pop();
				//从队列中的出口开始,宽搜目标点(x,y,z);
                for (int i = 0; i < 6; i++)
                {
                    int nx = dx[i] + t.x,ny = dy[i] + t.y,nz = dz[i] + t.z;

                    if(check({nx,ny,nz})) continue;
                    if(dist[nx][ny][nz] > dist[t.x][t.y][t.z] + 1)
                    {
                        dist[nx][ny][nz] = dist[t.x][t.y][t.z] + 1;
                        q.push({nx,ny,nz});
                    }
                }
            }
            cout << dist[x][y][z] << endl;
        }
    }

    return ;
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    please ac;
}

匿名函数的使用:
在这里插入图片描述

方法二(暴力):
(吐槽):
可以暴力过。但是很巧妙,起初我用的cin加速版搭配手写abs函数,再利用判断语句代替min函数,竟然AC了。之后全换上了scanf和printf竟然又不能过了,再换回cin和cout的加速也还是过不了,这也太神奇了吧。于是上网搜了下开氧气的代码,在牛客好像能用,但最好还是不要用。不过可以记住这行代码,毕竟有时候可能需要解决一些高复杂度的找规律题目,可以节省点时间,哈哈。

#pragma GCC optimize (2)//开氧气
#include<bits/stdc++.h>

using namespace std;
#define abs(x) ((x) < 0 ? -(x) : (x))
#define please return
#define ac 0

const int N = 1e5+10;
int a[3][N];

void solve()
{
    int n,m,h,q;
    int idx = 0;
    cin >> n >> m >> h >> q;
    while (q--)
    {
        int op,x,y,z;
        cin >> op >> x >> y >> z;
        if(op == 1)
        {
            a[0][idx] = x,a[1][idx] = y,a[2][idx++] = z;
        }
        else
        {
            int ans = 1e7;
            for (int i = 0; i < idx; i++)
            {
                int t = abs(x - a[0][i]) + abs(y - a[1][i]) + abs(z - a[2][i]);
                if(ans > t) ans = t;
            }
            printf("%d\n",ans);
        }
    }
    return ;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    please ac;
}

F gk的树

题目描述
jyq天天在公司摸鱼,感觉很无聊,突然想起了gk之前问过他的一个关于树的问题。
给你一颗树,每次操作你可以删掉一条边,最少需要多少次操作使得每个节点的度数都 <= k ?
输入描述:
第一行 T 表示一共有 T 组测试数据 (1≤T≤100000)
接下来一行输入给出的树的节点数 n 和度数限制 k (1 ≤ n,k ≤ 100000)
接下来 n−1 行输入相邻的两个节点 (u,v)。
Σn≤1e5
输出描述:
对于每一组测试数据输出最少的操作次数。
示例一:
输入:
2
4 2
1 2
2 3
2 4
6 2
1 2
1 3
1 4
4 5
4 6
输出:
1
1
说明:
第一组输入可以删掉 (1,2) 或 (2,3) 或 (2,4) 中的任意一条边。

第二组输入可以删掉 (1,4) 这条边。

#include<bits/stdc++.h>

using namespace std;
#define please return
#define ac 0

const int N = 1E5+10;

int h[N],e[N * 2],ne[N * 2],idx,d[N];//无向边
int n,k,res;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void dfs(int u,int fa)
{
    for(int i = h[u]; ~i;i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;//因为是无向边,那么如果重复遍历,则直接continue
        dfs(j,u);
    }

    if(d[u] > k)
    {
        res += d[u] - k;
        d[fa] --;
    }
}

void solve()
{
    cin >> n >> k;
    for (int i = 0; i < n - 1; i++)
    {
        int a,b;
        cin >> a >> b;
        add(a,b),add(b,a);//无向边
        d[a]++,d[b]++;
    }

    int root = 0;
    for(int i = 1;i <= n;i++)
        if(d[root] < d[i])  root = i;//找到度数最大的点

    res = 0;//初始化
    dfs(root,0);
    cout << res << endl;
    return;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        memset(d,0,sizeof(d));//初始化
        memset(h,-1,sizeof(h));//初始化
        solve();
    }
    please ac;
}

J 大数乘法

题目描述
给定整数x,y,你需要输出 xy mod p 的值
输入描述:
第一行一个整数t(1 ≤ t ≤ 10),表示接下来有 t 组测试用例,每个测试用例有三行整数x,y,p。
0 ≤ x ≤ 100000,0 ≤ y ≤ 10100000,100000 ≤ p ≤ 1000000007
输出描述:
输出t行,每行一个整数代表xy mod p 的值。
示例1
输入
5
3
4
998244353
2
10
998244353
0
100
998244353
1
100
998244353
4
100
1000000007
输出
81
1024
0
1
499445072
备注:
00 = 1
题解:
知识点:欧拉降幂
b ≥ φ(m) 时,a b ≡ a(bmodφ(m))+φ(m) mod m
b < φ(m) 时,a b ≡ ab mod m

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x)   ((x)&(-x))
#define fi first
#define se second
#define pb push_back

template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }

template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    x *= f;
}

const int N = 1e5+10;
ll x,p;
char str[N];

ll fpow(ll a,ll b)
{
    ll res = 1;
    while (b)
    {
        if(b & 1)   res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

ll get_euler(ll n)
{
    ll res = n;
    for (ll i = 2; i <= n / i; i++)
    {
        if(n % i == 0)
        {
            res = res / i * (i - 1);
            while(n % i == 0)   n /= i;
        }
    }
    if(n > 1)   res = res / n * (n - 1);
    return res;
}

int main()
{
    int t;
    read(t);
    while (t--)
    {
        scanf("%lld%s%lld",&x,str,&p);
        ll phi = get_euler(p);
        string s = str;
        ll y = 0;
        bool flag = false;
        for (int i = 0; i < s.size(); i++)
        {
            y = y * 10 + s[i] - '0';
            if(y >= phi)
            {
                y %= phi;
                flag = true;
            }
        }
        if(flag) y += phi;
        printf("%lld\n",fpow(x,y));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值