#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef string::const_iterator State; class ParseError {}; #define ll long long #define rep(i, s, n) for (ll i = (ll)(s); i < (ll)(n); i++) #define rrep(i, s, n) for (ll i = (ll)(s); i > (ll)(n); i--) #define all(a) (a).begin(), (a).end() #define rall(a) (a).rbegin(),(a).rend() #define mod 1000000007 #define P pair #define V vector #define C vector #define B vector #define endl '\n' const ll MAX = 510000; const ll MOD =998244353; using graph = vector>; int term(State &begin); int number(State &begin); int expression(State &begin); int factor(State &begin); struct edge{ //辺の重みを管理できるような構造体 //コンストラクタによって簡単に値を入れられるようにしている //operatorは辺の重みでソート出来るようにしている ll from, to; ll cost; ll capa; edge(ll s, ll d) : from(s), to(d) { cost = 0; capa = 0; } edge(ll s, ll d, ll w) : from(s), to(d), cost(w) { capa = 0; } edge(ll s, ll d, ll x, ll y) :from(s), to(d), cost(x), capa(y) {} bool operator < (const edge& x) const { return cost < x.cost; } }; using Graph=vector>;//重み付きグラフ //ダイクストラ法 vector Dijkstra(ll i, vector> Graph) { //i:始点 //Graph:重み付きグラフ ll n =Graph.size(); vector d(n, LONG_MAX); d[i] = 0; priority_queue< pair,//pair型 vector>,//内部コンテナ greater>//昇順 > q; q.push({0, i});//第一引数:コスト 第二引数:頂点 while (!q.empty()) { pair p = q.top(); q.pop(); int v = p.second; if (d[v] < p.first) { continue; } for (auto& x : Graph[v]) { if (d[x.to] > d[v] + x.cost) { d[x.to] = d[v] + x.cost; q.push({d[x.to], x.to}); } } } return d; } ll fac[MAX], finv[MAX], inv[MAX]; // テーブルを作る前処理 void cominit() { fac[0] = fac[1] = 1; finv[0] = finv[1] = 1; inv[1] = 1; for (ll i = 2; i < MAX; i++) { fac[i] = fac[i - 1] * i% MOD; inv[i] = MOD - inv[MOD % i] * (MOD / i)% MOD; finv[i] = finv[i - 1] * inv[i]% MOD; } } // mod. m での a の逆元 a^{-1} を計算する ll modinv(ll a, ll m) { ll b = m, u = 1, v = 0; while (b) { ll t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); } u %= m; if (u < 0) u += m; return u; } // 二項係数計算nCk,n<=10^7,k<=10^7まで ll com(ll n, ll k) { if (n < k) return 0; if (n < 0 || k < 0) return 0; return fac[n] * (finv[k] * finv[n - k]% MOD) % MOD; } //二項係数nCk,n<=10^9,k<=10^7まで ll com2(ll n,ll k){ ll res,bs=1,bb=1; for(ll i=0;i 0) { //cout<<"p="< 0) { //cout<<"p="< par; ll forest; UnionFind(ll n) : par(n, -1) {forest=n;} ll root(ll x) { if (par[x] < 0) return x; else return par[x] = root(par[x]); } bool issame(ll x, ll y) { return root(x) == root(y); } bool merge(ll x, ll y) { x = root(x); y = root(y); if (x == y){ return false; } if (par[x] > par[y]){ swap(x, y); // merge technique } forest--; par[x] += par[y]; par[y] = x; return true; } ll size(ll x) { return -par[root(x)]; } }; //素因数分解の関数 vector > prime_factorize(long long N) { vector > res; for (long long a = 2; a * a <= N; ++a) { if (N % a != 0) continue; long long ex = 0; // 指数 // 割れる限り割り続ける while (N % a == 0) { ++ex; N /= a; } // その結果を push res.push_back({a, ex}); } // 最後に残った数について if (N != 1) res.push_back({N, 1}); return res; } //約数列挙 vector enum_divisors(long long N) { vector res; for (long long i = 1; i * i <= N; ++i) { if (N % i == 0) { res.push_back(i); // 重複しないならば i の相方である N/i も push if (N/i != i){ res.push_back(N/i); } } } // 小さい順に並び替える sort(res.begin(), res.end()); return res; } //桁数 ll Keta(ll x){ ll cnt=1; while(x>=10){ x/=10; cnt++; } return cnt; } bool is_prime(long long N) { if (N == 1) return false; for (long long i = 2; i * i <= N; ++i) { if (N % i == 0) return false; } return true; } //多倍長整数対策 std::ostream &operator<<(std::ostream &dest, __int128_t value) { std::ostream::sentry s(dest); if (s) { __uint128_t tmp = value < 0 ? -value : value; char buffer[128]; char *d = std::end(buffer); do { --d; *d = "0123456789"[tmp % 10]; tmp /= 10; } while (tmp != 0); if (value < 0) { --d; *d = '-'; } int len = std::end(buffer) - d; if (dest.rdbuf()->sputn(d, len) != len) { dest.setstate(std::ios_base::badbit); } } return dest; } __int128 parse(string &s) { __int128 ret = 0; for (int i = 0; i < s.length(); i++) if ('0' <= s[i] && s[i] <= '9') ret = 10 * ret + s[i] - '0'; return ret; } //10の9乗+7でmodをとる template class modint { using u64 = std::uint_fast64_t; public: u64 a; constexpr modint(const u64 x = 0) noexcept : a(x % Modulus) {} constexpr u64 &value() noexcept { return a; } constexpr const u64 &value() const noexcept { return a; } constexpr modint operator+(const modint rhs) const noexcept { return modint(*this) += rhs; } constexpr modint operator-(const modint rhs) const noexcept { return modint(*this) -= rhs; } constexpr modint operator*(const modint rhs) const noexcept { return modint(*this) *= rhs; } constexpr modint operator/(const modint rhs) const noexcept { return modint(*this) /= rhs; } constexpr modint &operator+=(const modint rhs) noexcept { a += rhs.a; if (a >= Modulus) { a -= Modulus; } return *this; } constexpr modint &operator-=(const modint rhs) noexcept { if (a < rhs.a) { a += Modulus; } a -= rhs.a; return *this; } constexpr modint &operator*=(const modint rhs) noexcept { a = a * rhs.a % Modulus; return *this; } constexpr modint &operator/=(modint rhs) noexcept { u64 exp = Modulus - 2; while (exp) { if (exp % 2) { *this *= rhs; } rhs *= rhs; exp /= 2; } return *this; } }; //小数点12桁 struct all_init { all_init() { cout << fixed << setprecision(30); } } All_init; //Bit // 1-indexedなので注意。 struct BIT { private: vector bit; int N; public: BIT(int size) { N = size; bit.resize(N + 1); } // 一点更新です void add(int a, int w) { for (int x = a; x <= N; x += x & -x) bit[x] += w; } // 1~Nまでの和を求める。 int sum(int a) { int ret = 0; for (int x = a; x > 0; x -= x & -x) ret += bit[x]; return ret; } }; /* SegTree(n,fx): モノイド(集合X, 二項演算fx)についてサイズnで構築 set(int i, X x), build(): i番目の要素をxにセット。まとめてセグ木を構築する。O(n) update(i,x): i 番目の要素を x に更新。O(log(n)) query(a,b): [a,b) 全てにfxを作用させた値を取得。O(log(n)) */ template struct SegTree { using FX = function; int n; FX fx; vector dat; SegTree(int n_, FX fx_): n(), fx(fx_),dat(n_ * 4) { int x = 1; while (n_ > x) { x *= 2; } n = x; } void set(int i, X x) { dat[i + n - 1] = x; } void build() { for (int k = n - 2; k >= 0; k--) dat[k] = min(dat[2 * k + 1], dat[2 * k + 2]); } void update(int i, X x) { i += n - 1; dat[i] = x; while (i > 0) { i = (i - 1) / 2; // parent dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]); } } // the minimum element of [a,b) X query(int a, int b) { return query_sub(a, b, 0, 0, n); } X query_sub(int a, int b, int k, int l, int r) { if (r <= a || b <= l) { return pow(2,31); } else if (a <= l && r <= b) { return dat[k]; } else { X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2); X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r); return fx(vl, vr); } } /* debug */ inline X operator[](int a) { return query(a, a + 1); } void print() { for (int i = 0; i < n; ++i) { cout << (*this)[i]; if (i != n) cout << ","; } cout << endl; } }; struct Zip{ mapmp; Zip(vectora){ rep(i,0,a.size()){ mp[a[i]]=0; } ll size=0; for(auto& x:mp){//&はコンテナの値変更可能 x.second=size; size++; } } ll zip(ll n){ return mp[n]; } ll size(){ return mp.size(); } }; // aよりもbが大きいならばaをbで更新する // (更新されたならばtrueを返す) template bool chmax(T &a, const T& b) { if (a < b) { a = b; // aをbで更新 return true; } return false; } // aよりもbが小さいならばaをbで更新する // (更新されたならばtrueを返す) template bool chmin(T &a, const T& b) { if (a > b) { a = b; // aをbで更新 return true; } return false; } // 負の数にも対応した % 演算 long long modxx(long long val, long long m) { long long res = val % m; if (res < 0) res += m; return res; } /*二点間の距離*/ long double dist(pair a, pair b) { return sqrt(pow((a.first - b.first), 2) + pow((a.second - b.second), 2)); } /*二点間の距離*/ long double dist2(pair a, pair b) { return (a.first - b.first)*(a.first - b.first) + (a.second - b.second)*(a.second - b.second); } long double kt(pair a, pair b){ return abs((a.second-b.second)/(a.first-b.first)); } /* https://github.com/key-moon/Library/blob/master/src/Algorithm/rerooting.csx keymoon による C# の実装を noshi91 が C++ に移植したものです */ #include #include #include template class ReRooting { public: int NodeCount; private: std::vector> Adjacents; std::vector> IndexForAdjacent; std::vector Res; std::vector> DP; T Identity; std::function Operate; std::function OperateNode; public: ReRooting(int nodeCount, std::vector> edges, T identity, std::function operate, std::function operateNode) { NodeCount = nodeCount; Identity = identity; Operate = operate; OperateNode = operateNode; std::vector> adjacents(nodeCount); std::vector> indexForAdjacents(nodeCount); for (int i = 0; i < edges.size(); i++) { auto &edge = edges[i]; indexForAdjacents[edge[0]].push_back(adjacents[edge[1]].size()); indexForAdjacents[edge[1]].push_back(adjacents[edge[0]].size()); adjacents[edge[0]].push_back(edge[1]); adjacents[edge[1]].push_back(edge[0]); } Adjacents = std::vector>(nodeCount); IndexForAdjacent = std::vector>(nodeCount); for (int i = 0; i < nodeCount; i++) { Adjacents[i] = adjacents[i]; IndexForAdjacent[i] = indexForAdjacents[i]; } DP = std::vector>(Adjacents.size()); Res = std::vector(Adjacents.size()); for (int i = 0; i < Adjacents.size(); i++) DP[i] = std::vector(Adjacents[i].size()); if (NodeCount > 1) Initialize(); else if (NodeCount == 1) Res[0] = OperateNode(Identity, 0); } T Query(int node) { return Res[node]; } private: void Initialize() { std::vector parents(NodeCount); std::vector order(NodeCount); #pragma region InitOrderedTree int index = 0; std::stack stack; stack.push(0); parents[0] = -1; while (stack.size() > 0) { auto node = stack.top(); stack.pop(); order[index++] = node; for (int i = 0; i < Adjacents[node].size(); i++) { auto adjacent = Adjacents[node][i]; if (adjacent == parents[node]) continue; stack.push(adjacent); parents[adjacent] = node; } } #pragma endregion #pragma region fromLeaf for (int i = order.size() - 1; i >= 1; i--) { auto node = order[i]; auto parent = parents[node]; T accum = Identity; int parentIndex = -1; for (int j = 0; j < Adjacents[node].size(); j++) { if (Adjacents[node][j] == parent) { parentIndex = j; continue; } accum = Operate(accum, DP[node][j]); } DP[parent][IndexForAdjacent[node][parentIndex]] = OperateNode(accum, node); } #pragma endregion #pragma region toLeaf for (int i = 0; i < order.size(); i++) { auto node = order[i]; T accum = Identity; std::vector accumsFromTail(Adjacents[node].size()); accumsFromTail[accumsFromTail.size() - 1] = Identity; for (int j = accumsFromTail.size() - 1; j >= 1; j--) accumsFromTail[j - 1] = Operate(DP[node][j], accumsFromTail[j]); for (int j = 0; j < accumsFromTail.size(); j++) { DP[Adjacents[node][j]][IndexForAdjacent[node][j]] = OperateNode(Operate(accum, accumsFromTail[j]), node); accum = Operate(accum, DP[node][j]); } Res[node] = OperateNode(accum, node); } #pragma endregion } }; // グラフ、頂点の入次数、頂点数を受け取り、そのトポロジカルソートを記録した配列を返す関数 vector topological_sort(vector> &G2, vector &indegree, int V2) { // トポロジカルソートを記録する配列 vector sorted_vertices; // 入次数が0の頂点を発見したら、処理待ち頂点としてキューに追加する queue que; for (int i = 0; i < V2; i++) { if (indegree[i] == 0) { que.push(i); } } // キューが空になるまで、操作1~3を繰り返す while (que.empty() == false) { // キューの先頭の頂点を取り出す int v = que.front(); que.pop(); // その頂点と隣接している頂点の入次数を減らし、0になればキューに追加 for (int i = 0; i < G2[v].size(); i++) { int u = G2[v][i]; indegree[u] -= 1; if (indegree[u] == 0) que.push(u); } // 頂点vを配列の末尾に追加する sorted_vertices.push_back(v); } // トポロジカルソートを返す return sorted_vertices; } // 四則演算の式をパースして、その評価結果を返す。 int expression(State &begin) { int ret=term(begin); while(true){ if(*begin == '+'){ begin++; ret+=term(begin); } else if(*begin == '-'){ begin++; ret-=term(begin); } else{ break; } } cout<<"expr"< 0) { res = char(N % k + '0') + res; N /= k; } return res; } using namespace atcoder; using mint = modint998244353; int main() { ll n,m; cin>>n>>m; mapmp; rep(i,0,n){ string s; ll p; cin>>s>>p; mp[s]=p; } rep(i,0,m){ string s; ll p; cin>>s>>p; mp[s]=p; } for(auto &x:mp){ cout<