2021百度之星程序设计大赛-初赛一部分题目总结

1001迷失

  • 题目意思是要从 1 1 1号岛屿经过 k k k条路径到达 n n n号岛屿的概率,里面提出了一个附魔桥和普通桥的概念,经过附魔桥身上如果有附魔则附魔消失,如果没有附魔则被标记;到达 n n n号岛屿的时候,身上必须有附魔才能成功逃离,问成功逃离的概率
    解决这个问题需要几个知识点

邻接矩阵k次方的结论

  • 如果从 i i i j j j有路径,则设 a [ i ] [ j ] = 1 a[i][j]=1 a[i][j]=1,则矩阵 a k [ i ] [ j ] a^k[i][j] ak[i][j]表示从 i i i j j j经过 k k k条路径的通路总数

拆点法

  • 将每一个点拆成两个点,分别为有附魔的和没附魔的,可以分别表示为 i i i i + n i+n i+n,见下图
    在这里插入图片描述
  • 上面表示带附魔的,如果 i i i经过一条普通边,只需要在同侧连边;如果经过附魔边,则连向另外一侧
  • 这样以后只需要填充邻接矩阵先计算出所有点的度数,接下来根据每个点的的度数计算对模的逆元,之后进行矩阵快速幂计算即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 333;
const ll MOD = 998244353;
inline int read(){
	int x = 0, f = 1;char c = getchar();
	while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c=getchar();}
	return x*f;
}
struct Matrix{
	ll m[MAXN][MAXN];
	Matrix(){
		memset(m, 0, sizeof m);
	}
};
Matrix mul(Matrix a, Matrix b, int n){
	Matrix ans;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				ans.m[i][j] = (ans.m[i][j] + a.m[i][k] * b.m[k][j]) % MOD;
			}
		}
	}
	return ans;
}
Matrix fastpow(Matrix base, int power, int n){
	Matrix ans;
	for(int i=1;i<=n;i++) ans.m[i][i] = 1;
	while(power){
		if(power & 1) ans = mul(ans, base, n);
		base = mul(base, base, n);
		power >>= 1;
	}
	return ans;
}
void extend_gcd(ll a, ll b, ll &x, ll &y){
	if(b == 0){
		x = 1;
		y = 0;
		return;
	}
	extend_gcd(b, a % b, x, y);
	ll tmp = x;
	x = y;
	y = tmp - a / b * y;
}
ll inverse_mod(ll y, ll p){
	ll x, m;
	extend_gcd(y, p, x, m);
	return (p + x % p) % p;
}
int degree[MAXN];
ll inv[MAXN];
int main(){
	int t, n, m, k, u, v, w;
	t = read();
	while(t--){
		n = read();m = read();k = read();
		Matrix edge, mp;
		for(int i=0;i<m;i++){
			u = read();v = read();w = read();
			edge.m[u][v] = w + 1;
			edge.m[v][u] = w + 1;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(edge.m[i][j] == 2){
					++degree[i];
					++degree[j];
					++degree[i + n];
					++degree[j + n];
				}else if(edge.m[i][j] == 1) {
					++degree[i];
					++degree[j];
					++degree[i + n];
					++degree[j + n];
				}
			}
		}
		for(int i=1;i<=n;i++){
			degree[i] >>= 1;
			inv[i] = inverse_mod(degree[i], MOD);
			degree[i] = degree[i + n] = 0;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(edge.m[i][j] == 1){
					mp.m[i][j] = inv[i];
					mp.m[i + n][j + n] = inv[i];
				}else if(edge.m[i][j] == 2){
					mp.m[i + n][j] = inv[i];
					mp.m[i][j + n] = inv[i];
				}
			}
		}
		mp = fastpow(mp, k, 2 * n);
		cout << mp.m[1][2 * n] << '\n';
	}
	return 0;
}
  • 样例没问题不过交在HDU上面返回TLE,标程就是这个思路,可能是评测机的问题吧,我用榜 1 1 1的代码交了也是TLE,听说用矩阵计算这里用更高级的办法能把时间优化到 500 m s 500ms 500ms以下??

1003鸽子

  • d p [ i ] dp[i] dp[i]表示当前操作坏的电脑位置在 i i i的最少操作次数,那么因为当前操作是交换 i i i j j j,所以有 d p [ i ] = m i n ( d p [ i ] + 1 , d p [ j ] ) dp[i]=min(dp[i]+1,dp[j]) dp[i]=min(dp[i]+1,dp[j]) d p [ j ] = m i n ( d p [ j ] + 1 , d p [ i ] ) dp[j]=min(dp[j]+1,dp[i]) dp[j]=min(dp[j]+1,dp[i])
  • 很容易理解,注意更新不要覆盖
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int dp[MAXN];
int main(){
    ios::sync_with_stdio(false);
    int t, n, m, k, u, v;
    cin >> t;
    while(t--){
        cin >> n >> m >> k;
        memset(dp, 0x3f, sizeof dp);
        dp[k] = 0;
        for(int i=1;i<=m;i++){
            cin >> u >> v;
            int a = dp[u];
            int b = dp[v];
            dp[v] = min(b + 1, a);
            dp[u] = min(a + 1, b);
        }
        for(int i=1;i<=n;i++){
            if(i != 1) cout << ' ';
            cout << (dp[i] == INF ? -1 : dp[i]);
        }cout << '\n';
    }
    return 0;
}

1004萌新

  • 已知 a a a b b b c c c同余,给定 a a a b b b,让求 c c c的最小和最大值,满足 c − 1 > 0 c-1\gt0 c1>0 c ≤ m a x ( a , b ) c\leq max(a,b) cmax(a,b)
  • 现在假定 a > b a\gt b a>b,显然有 a − b = k c a-b=kc ab=kc,那么c最大时候显然是 k c kc kc也就是 a − b a-b ab,但是这里需要注意两种特殊情况分别是 a − b = 1 a-b=1 ab=1 a − b = 0 a-b=0 ab=0;当求c最小的时候只需要枚举 a − b a-b ab的每个因子,找到最小即可,但是需要注意特判 a = 1 , b = 1 a=1,b=1 a=1,b=1的两种情况
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
    ios::sync_with_stdio(false);
    int t;
    ll a, b;
    cin >> t;
    while(t--){
        cin >> a >> b;
        if(a < b) swap(a, b);
        ll c = a - b;
        if((a == 1 && b == 1) || c == 1){
            cout << -1 << " " << -1 << '\n';
            continue;
        }
        if(a == b){
            cout << 2 << " " << a << '\n';
            continue;
        }
        int f = 0;
        for(ll i=2;i*i<=c;i++){
            if(c % i == 0){
                cout << i << " ";
                f = 1;
                break;
            }
        }
        if(f) cout << c << '\n';
        else cout << c << ' ' << c << '\n';
    }
    return 0;
}

1006毒瘤数据结构题

  • 考察思维,用两个指针维护最左边的两个0,对于操作1,如果修改了第一个0,则更新最左边两个0的位置,如果修改了第二个0,则更新第二个0的位置,其他情况正常标记;对于操作2,如果查询到第一个0,那么答案为第二个0的位置,其他情况答案都为第一个0的位置
  • 在HDU上必须用超级快读才能过得了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e7 + 100;
const double eps = 1e-6;
int vis[MAXN];
inline int read(){
    int x = 0, f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}
    return x * f;
}
namespace IO{
    inline char nc(){
        static char buf[100000], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
    }
    template<typename T>
    inline T read(){
        char ch = nc();
        T sum = 0;
        while(ch < '0' || ch > '9'){
            ch = nc();
            if(ch == EOF) return EOF;
        }
        while(ch >= '0' && ch <= '9'){
            sum = (sum << 1) + (sum << 3) + (ch ^ 48);
            ch = nc();
            if(ch == EOF) return EOF;
        }
        return sum;
    }
}
int main(){
    ios::sync_with_stdio(false);
    int n, a, b;
    n = IO::read<int>();
    int x, y;
    x = 1;
    y = 2;
    for(int i=0;i<n;i++){
        a = IO::read<int>();
        b = IO::read<int>();
        if(a == 1){
            vis[b] = 1;
            if(b == y){
                while(vis[y]) ++y;
            }else if(b == x){
                x = y;
                ++y;
                while(vis[y]) ++y;
            }
        }
        else{
            if(b == x) printf("%d\n", y);
            else printf("%d\n", x);
        }
    }   
    return 0;
}

1008猎人杀

  • 狼人先杀人(也可能自刀),之后猎人开枪互相杀,直到杀死狼或者剩下人数不大于2
  • 暴力模拟即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t, n, x;
    cin >> t;
    while(t--){
        cin >> n;
        int id = -1;
        for(int i=1;i<=n;i++){
            cin >> x;
            if(x == 1) id = i;
        }
        vector<vector<int> > vs(n + 5);
        vector<int> vis(n + 5);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                cin >> x;
                vs[i].push_back(x);
            }
        }
        int ok = 0;
        int now = vs[id][0];
        int res = n - 1;
        vis[now] = 1;
        if(now == id){
            ok = 2;
        }else if(res <= 2){
            ok = 1;
        }
        else{
            while(1){
                if(ok) break;
                for(int i=0;i<vs[now].size();i++){
                    int to = vs[now][i];
                    if(vis[to] || to == now) continue;
                    vis[to] = 1;
                    now = to;
                    --res;
                    if(now == id){
                        ok = 2;
                    }else if(res <= 2){
                        ok = 1;
                    }
                    break;
                }                
            }
        }
        cout << (ok == 1 ? "langren" : "lieren") << '\n';
    }
    return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值