intwork(int x){ for(int i = 1; i <= 5; i ++ ) for(int j = i + 1; j <= 5; j ++ ) if((a[i] + a[j] - 1) % 10 + 1 == x) return x; return0; }
intmain(){ ios;
int T; cin >> T; while(T -- ){ int sum = 0; for(int i = 1; i <= 5; i ++ ) cin >> a[i], sum += a[i]; int x = (sum - 1) % 10 + 1; cout << work(x) << '\n'; }
return0; }
C. 秘神摩多罗
题目描述
给定一个完全图,每个点有一个点权 ai,两个点之间的边权为 ai−2×aj+c,其中 c 为常数。每次询问一个点集,求包含点集的最短简单路径长度。(保证 ai−2×aj+c 非负)「YAC Round 5」秘神摩多罗
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedeflonglong ll; constint N = 5e5 + 10; int n, m; int q[N], hh, tt; // 单调队列 deque容易超时
intmain(){ ios;
int T; cin >> T; while(T -- ){ cin >> n; vector<int> a(n + 2); // 增加一个a[n+1]=0作为虚拟点 for(int i = 1; i <= n; i ++ ) cin >> a[i];
cin >> m; vector<int> pre(n + 2); // [pre[i], i) 可以转移到 i for(int i = 1, l, r; i <= m; i ++ ){ // 预处理前缀最大值 cin >> l >> r; pre[r + 1] = max(pre[r + 1], l); } for(int i = 2; i <= n + 1; i ++ ) pre[i] = max(pre[i], pre[i - 1]);
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedeflonglong ll; constint N = 110, M = 2510;
structnode{int u, v, w;}e[M]; ll d[N][N][M]; int n, m, K;
intmain(){ ios;
memset(d, 0x3f, sizeof d); cin >> n >> m >> K; for(int i = 1; i <= n; i ++ ) d[i][i][0] = 0; for(int i = 1, u, v, w; i <= m; i ++ ) { cin >> u >> v >> w, e[i] = node{u, v, w}; d[u][v][0] = w; }
// 不使用魔法 for(int k = 1; k <= n; k ++ ) for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) d[i][j][0] = min(d[i][j][0], d[i][k][0] + d[k][j][0]);
// 最多使用一次魔法 for(int k = 1; k <= m; k ++ ){ auto [u, v, w] = e[k]; for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ){ if(d[i][j][0] < d[i][j][1]) d[i][j][1] = d[i][j][0]; // 不使用 d[i][j][1] = min(d[i][j][1], d[i][u][0] + d[v][j][0] - w); } }
// 最多使用K次魔法 for(int p = 2; p <= K; p ++ ) for(int k = 1; k <= n; k ++ ) for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) d[i][j][p] = min(d[i][j][p], d[i][k][p - 1] + d[k][j][1]); // i->k最多p-1次 k->j最多一次
cout << d[1][n][K] << '\n';
return0; }
但是 K≤106 ,不仅空间不够,而且明显会超时。不过到了这里,部分同学可能发现了先前状态转移有点像是快速幂。在快速幂中,我们把 p(幂次) 进行了二进制拆分,在本题我们可以将 d[i][j][p] 看作是所谓的 a 的 p 次方,将 d[i][j][1] 就看作是原始的 a,故我们可以使用 矩阵快速幂 来解决这个问题。
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) typedeflonglong ll; constint N = 110, M = 2510;
int n, m, K; ll d[N][N]; structnode{int u, v, w;}e[M]; structMatrix{ ll mat[N][N]; Matrix() { memset(mat, 0x3f, sizeof mat); } Matrix operator * (const Matrix &a){ // 运算符重载 Matrix res; for(int k = 1; k <= n; k ++ ) for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) res.mat[i][j] = min(res.mat[i][j], mat[i][k] + a.mat[k][j]); return res; } }ans, base;
// 矩阵快速幂 voidmat_qmi(int k){ while(k){ if(k & 1) ans = ans * base; base = base * base; k >>= 1; } }
voidfloyd(){ // 不使用魔法最短路 for(int k = 1; k <= n; k ++ ) for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) d[i][j] = min(d[i][j], d[i][k] + d[k][j]); // 最多用一次魔法得到的最短路 即base for(int k = 1; k <= m; k ++ ) { auto [u, v, w] = e[k]; for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) base.mat[i][j] = min(base.mat[i][j], min(d[i][j], d[i][u] + d[v][j] - w)); } }
voidinit(){ for(int i = 1; i <= n; i ++ ) for(int j = 1; j <= n; j ++ ) ans.mat[i][j] = d[i][j]; }
intmain(){ ios;
cin >> n >> m >> K;
memset(d, 0x3f, sizeof d); for(int i = 1; i <= n; i ++ ) d[i][i] = 0; for(int i = 1, u, v, w; i <= m; i ++ ){ cin >> u >> v >> w, e[i] = node{u, v, w}; d[u][v] = w; }