結果
| 問題 |
No.1785 Inequality Signs
|
| コンテスト | |
| ユーザー |
AngrySadEight
|
| 提出日時 | 2021-12-17 02:01:34 |
| 言語 | C++14 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 133 ms / 2,000 ms |
| コード長 | 17,685 bytes |
| コンパイル時間 | 2,361 ms |
| コンパイル使用メモリ | 193,832 KB |
| 実行使用メモリ | 6,944 KB |
| 最終ジャッジ日時 | 2024-09-14 02:16:54 |
| 合計ジャッジ時間 | 7,748 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge6 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 52 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define repr(i, n) for (int i = (int)(n); i >= 0; i--)
#define all(v) v.begin(), v.end()
#define mod1 1000000007
#define mod2 998244353
typedef long long ll;
vector<ll> bitconversion(ll i, ll n, ll N)
{
//数字iをn進数にして,長さNの配列にして返す。
//例えば,bitconversion(33,3,4)={1,0,2,0}。
ll x = 1;
rep(j, N)
{
x *= n;
}
vector<ll> vec(N);
rep(j, N)
{
x /= n;
vec[j] = i / x;
i -= x * vec[j];
}
return vec;
}
int ctoi(char c)
{
//char型の1桁の数をint型に変える。
switch (c)
{
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
default:
return 0;
}
}
vector<int> topological_sort(vector<vector<int>> &connection, vector<int> &count, int N)
{
//vectorをトポロジカルソートして返す。
//connectionにはそこからたどり着くことの出来る頂点の番号をあらかじめ入れておく。
//countにはその頂点に入ってくる頂点の数を入れておく。
vector<int> ans(0);
queue<int> que;
for (int i = 0; i < N; i++)
{
if (count[i] == 0)
{
que.push(i);
}
}
while (que.size() != 0)
{
int v = que.front();
que.pop();
for (int i = 0; i < connection[v].size(); i++)
{
count[connection[v][i]] -= 1;
if (count[connection[v][i]] == 0)
{
que.push(connection[v][i]);
}
}
ans.push_back(v);
}
return ans;
}
struct union_find
{
vector<int> par;
vector<int> rank;
vector<int> siz;
vector<int> siz2;
union_find(int N) : par(N), rank(N), siz(N), siz2(N)
{
rep(i, N)
{
par[i] = i;
rank[i] = 0;
siz[i] = 1;
siz2[i] = 0;
}
}
//宣言パート。union_findを宣言する時は,例えばunion_find tree(N)のようにする。
int root(int x)
{
if (par[x] == x)
{
return x;
}
return par[x] = root(par[x]);
}
//頂点に対して,その根となる頂点の番号を返す。
void unite(int x, int y)
{
int rx = root(x);
int ry = root(y);
if (rx == ry)
{
siz2[rx]++;
return;
}
if (rank[rx] < rank[ry])
{
par[rx] = ry;
siz[ry] += siz[rx];
siz2[ry] += siz2[rx];
siz2[ry]++;
}
else
{
par[ry] = rx;
siz[rx] += siz[ry];
siz2[rx] += siz2[ry];
siz2[rx]++;
if (rank[rx] == rank[ry])
rank[rx]++;
}
}
//頂点xとyをくっつけてやる。計算量削減のため,それぞれの木の「高さ」(rank)をカウントしておいて,
//その高さの高い方に低い方をくっつけてやるようにする。
bool same(int x, int y)
{
int rx = root(x);
int ry = root(y);
return rx == ry;
}
//頂点xとyが同じ木に属するかを判定する。
int size(int x)
{
return siz[root(x)];
}
//頂点xが含まれる木のサイズ。
int size2(int x)
{
return siz2[root(x)];
}
};
ll gcd(ll a, ll b)
{
//aとbの最大公約数。
ll r, temp;
if (a < b)
{
temp = a;
a = b;
b = temp;
}
while ((r = a % b) != 0)
{
a = b;
b = r;
}
return b;
}
ll iterative_square_method(ll i, vector<ll> &vec, ll N, ll mod)
{
//繰り返し二乗法。iのX乗をmodで割った余りを求める。
//なお,Xは,事前にmoddividing関数で長さNの二進数vector(vec)に直しておく必要がある。
//例えば,2の100000乗をmod1e9+7で求めたいときは,
//iterative_square_method(2,moddividing(100000,2,30),30,1000000007)のように書けば良い。
ll ans = 1;
rep(j, N)
{
if (vec[N - 1 - j] == 1)
ans = (ans * i) % mod;
i = (i * i) % mod;
}
return ans;
}
double iterative_square_method2(double X, vector<ll> &vec, ll N)
{
double ans = 1;
rep(j, N)
{
if (vec[N - 1 - j] == 1)
ans = ans * X;
X = X * X;
}
return ans;
}
ll moddividing(ll x, ll y)
{
//x÷yをmod1e9+7で求める。
vector<ll> vec(30);
rep(i, 30)
{
vec[i] = y;
y = y * y % 1000000007;
}
ll c = x;
c = c * vec[0] % 1000000007;
c = c * vec[2] % 1000000007;
c = c * vec[9] % 1000000007;
c = c * vec[11] % 1000000007;
c = c * vec[14] % 1000000007;
c = c * vec[15] % 1000000007;
c = c * vec[17] % 1000000007;
c = c * vec[19] % 1000000007;
c = c * vec[20] % 1000000007;
c = c * vec[23] % 1000000007;
c = c * vec[24] % 1000000007;
c = c * vec[25] % 1000000007;
c = c * vec[27] % 1000000007;
c = c * vec[28] % 1000000007;
c = c * vec[29] % 1000000007;
return c;
}
ll moddividing2(ll x, ll y)
{
//x÷yをmod998244353で求める。
vector<ll> vec(30);
rep(i, 30)
{
vec[i] = y;
y = y * y % 998244353;
}
ll c = x;
c = c * vec[0] % 998244353;
c = c * vec[1] % 998244353;
c = c * vec[2] % 998244353;
c = c * vec[3] % 998244353;
c = c * vec[4] % 998244353;
c = c * vec[5] % 998244353;
c = c * vec[6] % 998244353;
c = c * vec[7] % 998244353;
c = c * vec[8] % 998244353;
c = c * vec[9] % 998244353;
c = c * vec[10] % 998244353;
c = c * vec[11] % 998244353;
c = c * vec[12] % 998244353;
c = c * vec[13] % 998244353;
c = c * vec[14] % 998244353;
c = c * vec[15] % 998244353;
c = c * vec[16] % 998244353;
c = c * vec[17] % 998244353;
c = c * vec[18] % 998244353;
c = c * vec[19] % 998244353;
c = c * vec[20] % 998244353;
c = c * vec[21] % 998244353;
c = c * vec[22] % 998244353;
c = c * vec[24] % 998244353;
c = c * vec[25] % 998244353;
c = c * vec[27] % 998244353;
c = c * vec[28] % 998244353;
c = c * vec[29] % 998244353;
return c;
}
struct segtree
{
//セグメントツリー。いろんな機能があるよ。やったね。
vector<ll> vec;
segtree(ll N) : vec(N)
{
rep(i, N)
{
vec[i] = 0;
}
}
//初期化。セグメントツリーの正体はvectorである。
//バグが発生する可能性があるため,サイズNは(用意したいセグ木の長さ)*2よりも大きい2の累乗数で宣言してやる。
void make(ll k, ll a, ll N)
{ //0_indexed
k += (N / 2);
vec[k] = a;
}
//セグメントツリーのk番目の要素をaに変える。
void sum_update(ll k, ll a, ll N)
{ //0_indexed
k += (N / 2);
vec[k] = a;
while (k > 1)
{
k = k / 2;
vec[k] = vec[k * 2] + vec[k * 2 + 1];
}
}
//セグメントツリー上で和の計算を行う上での前処理。k番目の要素をaに変更して,なおかつ和の計算の前処理を行う。
void max_update(ll k, ll a, ll N)
{ //0_indexed
k += (N / 2);
vec[k] = a;
while (k > 1)
{
k = k / 2;
vec[k] = max(vec[k * 2], vec[k * 2 + 1]);
}
}
//セグメントツリー上で最大値を求める上での前処理。k番目の要素をaに変更して,なおかつ最大値を求める前処理を行う。
void min_update(ll k, ll a, ll N)
{ //0_indexed
k += (N / 2);
vec[k] = a;
while (k > 1)
{
k = k / 2;
vec[k] = min(vec[k * 2], vec[k * 2 + 1]);
}
}
//セグメントツリー上で最小値を求める上での前処理。k番目の要素をaに変更して,なおかつ最小値を求める前処理を行う。
ll min_query(ll a, ll b, ll k, ll l, ll r)
{ // min_query(a,b,1,0,N / 2) a,b,l,r=0_indexed
if (r <= a || b <= l)
return 1000000000;
if (a <= l && r <= b)
return vec[k];
ll vl = min_query(a, b, k * 2, l, (l + r) / 2);
ll vr = min_query(a, b, k * 2 + 1, (l + r) / 2, r);
return min(vl, vr);
}
//セグメントツリー上で最小値を求める。半開区間[a,b)(a,bは0_indexed)における最小値を求めてやる。
//k,l,rは固定で,k=1,l=0,r=N/2として良い。
ll check(ll i, ll N)
{
return vec[i + N / 2];
}
//i番目の要素を確認する。
ll max_query(ll a, ll b, ll k, ll l, ll r)
{ // max_query(a,b,1,0,N / 2) a,b,l,r=0_indexed
if (r <= a || b <= l)
return -100000000000000000;
if (a <= l && r <= b)
return vec[k];
ll vl = max_query(a, b, k * 2, l, (l + r) / 2);
ll vr = max_query(a, b, k * 2 + 1, (l + r) / 2, r);
return max(vl, vr);
}
//セグメントツリー上で最大値を求める。半開区間[a,b)(a,bは0_indexed)における最大値を求めてやる。
//k,l,rは固定で,k=1,l=0,r=N/2として良い。
ll sum_query(ll a, ll b, ll k, ll l, ll r)
{ // sum_query(a,b,1,0,N / 2) a,b,l,r=0_indexed
if (r <= a || b <= l)
return 0;
if (a <= l && r <= b)
return vec[k];
ll vl = sum_query(a, b, k * 2, l, (l + r) / 2);
ll vr = sum_query(a, b, k * 2 + 1, (l + r) / 2, r);
return vl + vr;
}
//セグメントツリー上で和を求める。半開区間[a,b)(a,bは0_indexed)における和を求めてやる。
//k,l,rは固定で,k=1,l=0,r=N/2として良い。
};
char itoc(int c)
{
//int型の0から25までの値cを,(c+1)番目のchar型アルファベット1文字に変更してやる。
switch (c)
{
case 0:
return 'a';
case 1:
return 'b';
case 2:
return 'c';
case 3:
return 'd';
case 4:
return 'e';
case 5:
return 'f';
case 6:
return 'g';
case 7:
return 'h';
case 8:
return 'i';
case 9:
return 'j';
case 10:
return 'k';
case 11:
return 'l';
case 12:
return 'm';
case 13:
return 'n';
case 14:
return 'o';
case 15:
return 'p';
case 16:
return 'q';
case 17:
return 'r';
case 18:
return 's';
case 19:
return 't';
case 20:
return 'u';
case 21:
return 'v';
case 22:
return 'w';
case 23:
return 'x';
case 24:
return 'y';
case 25:
return 'z';
default:
return 'a';
}
}
ll kruskal(vector<pair<ll, pair<int, int>>> &connect, union_find tree)
{
//クラスカル法で,1辺を結ぶ2頂点の番号とその辺の長さが与えられた場合に
//最小全域木を作る場合の辺の長さの総和を求める。
//connect関数には,「1辺の長さ」「2頂点の番号」をあらかじめ入れておく。
ll ans = 0;
for (ll i = 0; i < connect.size(); i++)
{
if (!tree.same(connect[i].second.first, connect[i].second.second))
{
ans += connect[i].first;
tree.unite(connect[i].second.first, connect[i].second.second);
}
}
return ans;
}
void dijkstra(vector<vector<pair<ll, ll>>> &G, ll s, vector<ll> &dis)
{
//ダイクストラ法。sを始点としたときの,最短距離を求める。
//ここでは,Gに「第一要素=行き先,第二要素=コスト」として,無効グラフを考える。
//Nには構成されるグラフの頂点の数が入る。
//計算量は,辺の数をE,頂点の数をVとして,O(ElogV)である。
//priority_queueの要素は「第一要素=距離,第二要素=頂点の番号」だから注意!
priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>> pque;
dis[s] = 0;
pque.push(pair<ll, ll>(dis[s], s));
while (!pque.empty())
{
pair<ll, ll> p = pque.top();
pque.pop();
ll v = p.second;
if (dis[v] < p.first)
continue;
for (int i = 0; i < G[v].size(); i++)
{
if (dis[G[v][i].first] > dis[v] + G[v][i].second)
{
dis[G[v][i].first] = dis[v] + G[v][i].second;
pque.push(pair<ll, ll>(dis[G[v][i].first], G[v][i].first));
}
}
}
}
vector<vector<ll>> matrix_exponentiation(vector<vector<ll>> &M, vector<ll> &bin, ll N, ll K, ll mod)
{
//行列累乗。N*N行列MのX乗を求める。ただし,Xは,事前にbitconversion関数で2進数に変えておき,その配列binを渡す必要あり。
//NにはMの行列のサイズを,Kにはbin配列のサイズを渡す。また,modを取る場合は変数modにその値を渡す。
vector<vector<ll>> matrix(N, vector<ll>(N));
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
matrix[i][j] = M[i][j];
}
}
vector<vector<ll>> ans_old_matrix(N, vector<ll>(N));
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
if (i == j)
ans_old_matrix[i][j] = 1;
else
ans_old_matrix[i][j] = 0;
}
}
vector<vector<ll>> ans_new_matrix(N, vector<ll>(N));
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
ans_new_matrix[i][j] = 0;
}
}
vector<vector<ll>> new_matrix(N, vector<ll>(N));
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
new_matrix[i][j] = 0;
}
}
for (ll x = 0; x < K; x++)
{
if (bin[K - 1 - x] == 1)
{
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
for (ll k = 0; k < N; k++)
{
ans_new_matrix[i][j] = (ans_new_matrix[i][j] + (ans_old_matrix[i][k] * matrix[k][j])) % mod;
}
}
}
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
ans_old_matrix[i][j] = ans_new_matrix[i][j];
ans_new_matrix[i][j] = 0;
}
}
}
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
for (ll k = 0; k < N; k++)
{
new_matrix[i][j] = (new_matrix[i][j] + (matrix[i][k] * matrix[k][j])) % mod;
}
}
}
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < N; j++)
{
matrix[i][j] = new_matrix[i][j];
new_matrix[i][j] = 0;
}
}
}
return ans_old_matrix;
}
void bellman_ford(vector<ll> &from, vector<ll> &to, vector<ll> &cost, vector<ll> &dis, ll v, ll N, ll M, bool &negative)
{
//ベルマンフォード法。
//頂点fromから頂点toまでの長さcostの辺が,M本張られている。頂点数はN。
//このもとで,始点vからの最短距離disを求めることにする。
//負の閉路がある場合はnegativeがtrueになる。
vector<bool> hasroute(N, false);
hasroute[v] = true;
vector<bool> nega(N, false);
for (ll i = 0; i < N; i++)
{
bool update = false;
for (ll j = 0; j < M; j++)
{
if (dis[to[j]] > dis[from[j]] + cost[j])
{
dis[to[j]] = dis[from[j]] + cost[j];
if (hasroute[from[j]])
hasroute[to[j]] = true;
update = true;
if (i == N - 1)
{
nega[to[j]] = true;
}
}
}
if (!update)
{
break;
}
}
for (ll i = 0; i < N; i++)
{
for (ll j = 0; j < M; j++)
{
if (hasroute[from[j]] && nega[from[j]])
nega[to[j]] = true;
}
}
if (hasroute[N - 1] && nega[N - 1])
negative = true;
}
int main()
{
ll N, K;
cin >> N >> K;
vector<ll> factorial(N + 2);
factorial[0] = 1;
for (ll i = 0; i < N + 1; i++)
{
factorial[i + 1] = (factorial[i] * (i + 1)) % mod1;
}
ll comb = 1;
for (ll i = 0; i < N; i++)
{
comb = (comb * (K + N - 1 - i)) % mod1;
}
ll ans = 0;
for (ll i = 0; i <= N - 1; i++)
{
ll combination = moddividing(factorial[N - 1], (factorial[N - 1 - i] * factorial[i]) % mod1) % mod1;
ll X = moddividing(comb, factorial[N]);
ans = (ans + (X * combination) % mod1) % mod1;
comb = moddividing(comb, K + N - 1 - i);
comb = (comb * (K - 1 - i)) % mod1;
//cout << comb << " " << combination << " " << ans << endl;
//cout << comb << " " << ans << endl;
}
cout << ans << endl;
}
AngrySadEight