記事ディレクトリ
A. 線上のバッタ
1. 問題点
nnを指定するとnとkき、んn はmmと書くことができますm個の数値の合計ですが、タイトルではこれらの数値をkkkの倍数次に、最小のmmm、このmm をm桁。
2. 分析
もしnnnはkkではありませんkの倍数。ここではnn を直接出力できます。n、つまりm = 1 m=1メートル=1。
もしnnnはkですkの倍数。 nnを使用する必要があることを示します。n分割します。結論は次のとおりです。この場合、nnnは 2 つの数値に分割するだけで済みます。
証明:
なぜなら、nはkですkの倍数では、 n = a ∗ kn=a*kと仮定できます。n=ある∗k。
如果 a ≥ 2 a\geq 2 ある≥2
次に、 p + q = a p+q=aを作ることができます。p+q=a ,n = ( p + q ) ∗ k = p ∗ k + q ∗ kn=(p+q)*k=p*k+q*kn=( p+q )∗k=p∗k+q∗k、この時点でans 1 = p ∗ k + 1 ans_1=p*k+1s _1=p∗k+1、ans 2 = q ∗ k − 1 ans_2=q*k-1s _2=q∗k−1、タイトルでk ≥ 2 k \geq 2k≥2.したがって、構築されたpppとqqqはkkにはなりませんkの倍数
a < 2の場合a < 2ある<2,即 a = 1 a=1 ある=1 、この時点では、 p = 1 、 q = n − 1 p=1,q=n-1 とすることができます。p=1 、q=n−1で十分です。
3. コード
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n, k;
cin >> n >> k;
if(n % k != 0)
{
cout << 1 << endl;
cout << n << endl;
}
else
{
cout << 2 << endl;
int a = n / k;
if(a == 1)
cout << 1 << " " << n - 1 << endl;
else
cout << (a - 1) * k + 1 << " " << k - 1 << endl;
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--)
solve();
}
B. 比較文字列
1. 問題点
与えられたものを「<」「<」で与える』<「と」 > 「 >」』>'によって形成される文字列 S。S[i]が ' > ' >'を意味する場合'>'、対応する a 配列は次を満たす必要があります:a [ i ] > a [ i + 1 ] a[i]>a[i+1]ア[イ]>ア[私+1 ]で、最小の数で配列を構築し、異なる数の数を出力してください。
2. 分析
上記の例によれば、「>」または「<」を連続して配置すると要素の数が増加することがわかります。反対の符号が現れたときは、現れた数字を使ってそれを組み立てることができます。
上記の例からそのような規則を見つけるのは難しくありません。
「>」と「<」はそれぞれ最長の連続部分文字列に対応し、長さはppとして記録されます。pとqqq . 答えは次のようになります。ans = max ( p , q ) + 1 ans=max(p,q)+1s _=max ( p , _q )+1。
3. コード
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
string s;
cin >> n >> s;
int ans = 0;
for(int i = 0; i < n; i ++)
{
int j = i;
while(j < n && s[j] == s[i])
j ++;
ans = max(ans, j - i);
i = j - 1;
}
cout << ans + 1 << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--)
solve();
}
C. ベストバイナリ文字列
1. 問題点
文字列が与えられると、この文字列は 3 つの文字 1、0、? で構成され、? の部分に 0 または 1 を入力でき、それぞれの? は独立しています。つまり、特定の? の入力選択には影響しません。後続?。次に、文字列内の各 ? に値を割り当てる必要があります。01 01を取得01文字列、次に01 01の場合01文字列は次のように操作できます。反転する連続した部分文字列を選択します。複数の操作を行った後、文字列を非降順文字列に並べ替えることができます。このプロセスでは、多数の操作が取得されます。次に、最小数の操作01 0101弦。
2. 分析
タイトルによると、複数の操作の後、文字列は次のようになります。
つまり、すべての 1 を結合し、すべての 0 を結合する必要があります。
上の例からわかるように、連続しない 1 がある限り、1 つの操作が必要です。つまり、連続する 1 の文字列が何個存在するかについて、複数回の反転を実行する必要があります。私たちの目的は、連続する 1 の部分文字列の数を最小限に抑えることです。。
上記の結論から、 "1???1"の形式の部分文字列が存在する限り、中央の疑問符は数字 1 として記述する必要があることがわかります。
**"0???1"または"1???0"**の場合、真ん中のすべての疑問符が 0 または 1 で埋められているかどうかは関係ありません。次に、先頭が 0 の場合は 0 を書き込み、先頭が 1 の場合は 1 を書き込むと規定できます。この目的は、コードをより適切に記述することです。
「???0」の先頭に疑問符が表示される場合は、すべて0を記述する必要があります。全部1を書くと1の部分文字列が増えるからです。
先頭が "???1" の場合、すべて 0 またはすべて 1 を書いても 1 の部分文字列の数には影響しないため、0 を書き込むように指定できます。この場合、最初の状況を統一して、すべて 0 を書き込むことができます。
最後に疑問符「1???」が表示される場合は、すべて 1 と入力します。
末尾が「0???」の場合は全て0を書き込みます。つまり、先頭には何でも書きます。
したがって、上記のすべては次のアプローチに要約できます。
? で始まる場合 、0に変更するだけで、残りは?すべて前のキャラクターと一致しています。(上記の分類の議論に従って自分で確認できます)
3. コード
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve()
{
string s;
cin >> s;
if(s[0] == '?')
s[0] = '0';
for(int i = 0; i < s.size() - 1; i ++)
if(s[i + 1] == '?')
s[i + 1] = s[i];
cout << s << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
}
D. ブラケットの色付け
1. 問題点
括弧シーケンスが与えられた場合、その括弧シーケンスが一致できる場合 (例: "()()"、"(()())"、"((()))"、またはその逆であれば、一致させることができます。 : ")("、"))(("、これを美しいブラケットシーケンスと呼びます。
次に、ランダムなブラケット シーケンスを与えます。このブラケット シーケンスに色を付ける必要があります。同じ色のブラケットで美しいブラケット シーケンスを形成する必要があります。
ここで、最低限必要な色とカラーリング戦略を出力する必要があります。どんなに色を塗っても美しい括弧の連続が形成できない場合は、-1を出力します。
2. 分析
左右の括弧を 1 つずつ照合する必要があるため、左の括弧の数が右の括弧の数と等しくない場合、答えを構築して -1 を直接出力することはできません。
左括弧の数が右括弧の数と等しい場合、それを構築する必要があります。なぜ?
左右の括弧のうち、「)(」、「()」は美しいので、それぞれの括弧に色を付けると、最悪の場合、 n 2 \frac{ n }{2}になります。2ん美しいブラケット。したがって、解決策があるはずです。
解決策があるかどうかを検討したら、次に最適な解決策を構築する方法を検討します。
ここでは結論を先に述べてから、色の数が 1 か 2 であることを証明します。
1の場合は単純で、入力されたブラケットシーケンス自体が美しい場合は、1色に染めるだけで済みます。
次に、2 つの色の状況を見てください。
上の図の状況であれば、少なくとも 2 つの色があることは理解できます。
左の写真の場合、奥の赤いブラケットを手前の青いブラケットに統一できます。残りの状況は同じです。
次の問題は色付けの問題です。
括弧の一致の問題については、通常、スタックを使用して解決します。最初のかっこをスタックに置きます。最初のかっこと同じかっこが現れた場合は、それをスタックに置き、一致するかっこがある場合は、スタックの一番上からかっこを取り出してそれに一致させ、一番上のかっこを削除します。スタックの 。
スタック内の括弧と一致する括弧が見つかったものの、スタックが空の場合は、別の色でペイントする必要があります。最終スタックに残っている括弧も別の色でペイントする必要があります。
3. コード
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve()
{
int n;
string s;
cin >> n >> s;
int nums = 0;
for(int i = 0; i < n; i ++)
if(s[i] == '(')
nums ++;
if(2 * nums != n)
{
cout << -1 << endl;
return;
}
string str = "";
for(int i = n - 1; i >=0; i --)
str.push_back(s[i]);
vector<int>ans(n, 0);
stack<pair<char,int>>stk1;
for(int i = 0; i < n; i ++)
{
if(s[i] == s[0])
stk1.push({
s[i], i});
else
{
if(stk1.empty())
ans[i] = 2;
else
{
auto t = stk1.top();
stk1.pop();
ans[t.second] = 1;
ans[i] = 1;
}
}
}
while(stk1.size())
{
auto t = stk1.top();
stk1.pop();
ans[t.second] = 2;
}
int x = 0;
for(int i = 0; i < n; i ++)
x += ans[i];
if(x == n || x == 2 * n)
{
cout << 1 << endl;
for(int i = 0; i < n; i ++)
cout << 1 << " ";
cout << endl;
return;
}
cout << 2 << endl;
for(auto x : ans)
cout << x << " ";
cout << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
}
E. プレーオフの八百長
1. 問題点
2. 分析
3. コード
F. 二人のための社説
1. 問題点
nnを指定するとnとkk、長さnnn配列。今、 nnからn 個の数字の中からkkを選び出すK 個の数は部分列と呼ばれます。次に、このサブシーケンスを 2 つの部分に分割し、サブシーケンス 1 とサブシーケンス 2 として示します。次に、サブシーケンス 1 とサブシーケンス 2 の両方に対応する合計があります。これら 2 つの合計を最大値と比較できます。ここで必要なのは、最大値の最小値。
2. 分析
これは非常に一般的なルーチンです。最大値の最小値、または最小値の最大値を求める問題が発生した場合、通常は二分法を使用してそれを解決します。
最終的な答えはここで分けます。
2点はチェックチェックが難しいチェック関数の記述。
この質問では、チェックチェックしてくださいチェック機能の機能は、二項対立の過程でミッドミッドを判断することです。中間値は妥当ですか。
次に、これは最大でもk 1 k1を選択します。k 1数字悔い改めとMid Mid未満の場合です。Midの場合はできるだけ多くの数値を選択し、Mid MidMid の場合、選択した数値の最大値を削除します。その目的は、より多くの数値を選択するためのより多くのスペースを残すことであり、最大値を選択するこのプロセスは、大規模なルート ヒープを使用して最適化できます。
さらに、図内のプレフィックスとサフィックスの分割位置が不明確であるため、実行可能な解決策を見つけるには、すべての分割位置を列挙する必要があります。
したがって、最初に、悔い改めと貪欲を使用して、すべての接頭辞と接尾辞の中で最も選択された数k 1 、k 2 k1、k2を前処理できます。k1 、_k 2。次に、分割位置を列挙し、 k 1 + k 2 ≥ k k1+k2 \geq kのグループがあるかどうかを判断します。k1 _+k2_ _≥k。
复杂度为: O ( l o g ( s u m ) ∗ ( n l o g n + n ) ) = O ( l o g ( s u m ) n l o g n ) O(log(sum)*(nlogn+n))=O(log(sum)\ n\ logn) O ( log (合計) _ _ _ _∗( nログn _ _+n ))=O ( log ( sum ) n log n ) _ _ _ _ _ _
3. コード
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
bool check(int maxv, int n, vector<int>a, int k)
{
vector<int>f(n + 1, 0), g(n + 1, 0);
priority_queue<int>q, qq;
int sum = 0;
for(int i = 0; i < n; i ++)
{
if(sum + a[i] <= maxv)
{
sum += a[i];
q.push(a[i]);
f[i + 1] = f[i] + 1;
}
else
{
q.push(a[i]);
sum += a[i];
sum -= q.top();
q.pop();
f[i + 1] = f[i];
}
}
sum = 0;
reverse(a.begin(), a.end());
for(int i = 0; i < n; i ++)
{
if(sum + a[i] <= maxv)
{
sum += a[i];
qq.push(a[i]);
g[i + 1] = g[i] + 1;
}
else
{
sum += a[i];
qq.push(a[i]);
sum -= qq.top();
qq.pop();
g[i + 1] = g[i];
}
}
for(int i = 1; i <= n; i ++ )
{
if(f[i] + g[n - i] >= k)
return true;
}
return false;
}
void solve()
{
int n, k;
cin >> n >> k;
vector<int>a(n);
int sum = 0;
for(int i = 0; i < n; i ++)
{
cin >> a[i];
sum += a[i];
}
int l = 0, r = sum;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid, n, a, k))
r = mid;
else
l = mid + 1;
}
cout << l << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
}