結果
| 問題 |
No.2251 Marking Grid
|
| コンテスト | |
| ユーザー |
bayashiko
|
| 提出日時 | 2025-02-01 12:34:47 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 755 ms / 3,000 ms |
| コード長 | 3,827 bytes |
| コンパイル時間 | 5,494 ms |
| コンパイル使用メモリ | 216,056 KB |
| 実行使用メモリ | 45,620 KB |
| 最終ジャッジ日時 | 2025-02-01 12:35:01 |
| 合計ジャッジ時間 | 13,338 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 40 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 998244353;
// Union-Find (Disjoint Set Union) の実装
struct DSU {
int n;
vector<int> parent, rank;
DSU(int n): n(n), parent(n), rank(n, 0) {
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int a) {
if(parent[a] == a) return a;
return parent[a] = find(parent[a]);
}
bool merge(int a, int b) {
a = find(a), b = find(b);
if(a == b) return false;
if(rank[a] < rank[b]) swap(a, b);
parent[b] = a;
if(rank[a] == rank[b]) rank[a]++;
return true;
}
};
// 累乗 (mod MOD)
long long modexp(long long base, long long exp){
long long result = 1;
base %= MOD;
while(exp > 0){
if(exp & 1) result = (result * base) % MOD;
base = (base * base) % MOD;
exp >>= 1;
}
return result;
}
// 辺の構造体:セル (i,j) に対応.
struct Edge {
int u, v; // u: 行 (0〜H-1), v: 列 (内部的には H〜H+W-1)
int w; // セルに書かれている値
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int H, W;
cin >> H >> W;
vector<vector<int>> A(H, vector<int>(W));
vector<int> candVals;
vector<Edge> edges;
for (int i = 0; i < H; i++){
for (int j = 0; j < W; j++){
cin >> A[i][j];
candVals.push_back(A[i][j]);
Edge e;
e.u = i;
e.v = H + j; // 列の頂点番号は H から始まる
e.w = A[i][j];
edges.push_back(e);
}
}
// 重複を除いた候補値
sort(candVals.begin(), candVals.end());
candVals.erase(unique(candVals.begin(), candVals.end()), candVals.end());
// 辺を重み (セルの値) の降順にソート
sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b){
return a.w > b.w;
});
// 候補値も降順・昇順両方で使うので,ここでは降順版も作っておく
vector<int> candDesc = candVals;
sort(candDesc.begin(), candDesc.end(), greater<int>());
int totalVertices = H + W;
DSU dsu(totalVertices);
int comp = totalVertices; // 初期は各頂点が独立
int eidx = 0;
// 各候補 T に対して,グラフ(セルの値 > T の辺による)の連結成分数を記録
unordered_map<int,int> compFor;
for (int T : candDesc) {
// 辺リストから,重みが T より大きい辺を追加
while(eidx < edges.size() && edges[eidx].w > T){
if(dsu.merge(edges[eidx].u, edges[eidx].v)){
comp--;
}
eidx++;
}
compFor[T] = comp;
}
// 事前に 2 のべき乗を 0 〜 totalVertices まで計算しておく
vector<long long> pow2(totalVertices+1, 1);
for (int i = 1; i <= totalVertices; i++){
pow2[i] = (pow2[i-1] * 2) % MOD;
}
// f(T) = 2^(連結成分数 - 1) - 1
auto f = [&](int T) -> long long {
int c = compFor[T];
long long ways = (pow2[c - 1] - 1) % MOD;
if(ways < 0) ways += MOD;
return ways;
};
// 印の付け方のうち「すべての印が T 以下」であるものの個数は f(T)
// よって,最大値が正確に T となる印の付け方の個数は,(f(T) - f(prev)) となる.
// 最小値より下では f = 0 とみなせる.
long long ans = 0;
long long prevF = 0;
// 候補を昇順に処理
sort(candVals.begin(), candVals.end());
for (int T : candVals) {
long long curF = f(T);
long long diff = (curF - prevF) % MOD;
if(diff < 0) diff += MOD;
ans = (ans + (1LL * T * diff) % MOD) % MOD;
prevF = curF;
}
cout << ans % MOD << "\n";
return 0;
}
bayashiko