Codeforces Round#673(Div。2)AE問題の解決策
コンテストリンク:https://codeforces.com/contest/1417
質問A
水、貪欲
問題は、長さnのシーケンスが与えられ、上限kが与えられた場合、元のシーケンスの各番号は1からkの間であり、操作するたびに現在のシーケンスに番号を追加できることを意味します。別の数に、しかし値をkを超えさせてはいけないので、今度は最大で何回操作できるか尋ねてください。
値が最も小さいものを直接選択し、この最小値でできるだけ多くの他の数値を追加します。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
int32_t main()
{
IOS;
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
int ans=0,tar=0;
vector<int>num(n);
for(int i=0;i<n;i++)
{
cin>>num[i];
if(num[i]<num[tar]) tar=i;
}
for(int i=0;i<n;i++)
if(i!=tar) ans+=(k-num[i])/num[tar];
cout<<ans<<endl;
}
}
質問B
貪欲、構造
問題は、長さnのシーケンスが与えられ、値Tが与えられた場合、元のシーケンスを2つのシーケンスに分割する必要があることを意味します。同じ番号のシーケンス内の、合計がTになる2つの番号のシーケンスの添え字の数は最小です。
貪欲になりましょう。Tの半分を最初のシリーズに入れ、Tよりも大きい半分を2番目のシリーズに入れます。Tの半分は2つ未満のシリーズに均等に分割できます。Tよりも小さい半分をTよりも大きい半分に加算してTに等しくすることしかできないため、単純に2つの部分に分割できます。均一な分布の半分に等しい結論は、原稿で計算できます。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
int32_t main()
{
IOS;
int t;
cin>>t;
while(t--)
{
ll n,T;
cin>>n>>T;
bool flag=0;
vector<bool>color(n);
for(int i=0;i<n;i++)
{
ll x;
cin>>x;
if(x*2==T) {
color[i]=flag;flag=!flag;}
else if(x*2<T) color[i]=0;
else color[i]=1;
}
for(int i=0;i<n;i++) cout<<color[i]<<' ';
cout<<endl;
}
}
C問題
文字列、小さな結論、マップの簡単な使用
長さnのシーケンスが与えられた場合、値1-nのペアとkの値、シーケンス内の長さkのペアとすべての連続サブシーケンス、および共通の最小数を出力する必要があります。共有番号がない場合は-1を出力します。
正面から直接考えると、考えるのが難しいでしょう。特定の値から、この値を特定の長さkの最小共通値として使用する場合、長さkは、値がシリーズに表示されるすべての位置で隣接する位置間の最大距離以上である必要があることがわかります(注シーケンスの左端と右端は特別に判断されます)。この結論を使用して、mapを使用して、forループの各値に対応する最小kを取得できます。記録後、答えとしてkforで1-nを直接実行し、最小kを出力します。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
int32_t main()
{
IOS;
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
map<int,pair<int,int>>M;//M[i]=pair{j,k},代表值为i的数字最后出现在下标j的位置,值为i的数字之间的最大间隔为k
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
if(M.find(x)==M.end()) M[x]=make_pair(i,i);//数列左侧特判
else
{
M[x].second=max(M[x].second,i-M[x].first);
M[x].first=i;
}
}
vector<int>ans(n+1,INF);
for(auto &x:M)
{
x.second.second=max(x.second.second,n+1-x.second.first);//特判数列右侧
ans[x.second.second]=min(ans[x.second.second],x.first);
}
int now=INF;
for(int i=1;i<=n;i++)
{
now=min(now,ans[i]);
if(now==INF) cout<<-1<<' ';
else cout<<now<<' ';
}
cout<<endl;
}
}
質問Dの
構造、思考
問題は、長さnのシーケンスが与えられた場合、各数値の値は1から1e5までの正の整数であり、このシーケンスの各数値の値を最大3n回の操作にする必要があることを意味します。等しい。
操作ごとに、2つの添え字iとjを選択し、0から1e9までの自然数xを選択して、aiからi ×\ timesを差し引くことができます。× x、ajにi×\ timesを追加× x。
まず、操作中にシーケンス全体の合計が変更されていないことに注意してください。したがって、シーケンス全体の合計は、シーケンス長nの整数で構成する必要があります。次に、ここでの操作の特殊性は、2つの添え字の値に添え字iを乗算する必要があることに気付きました。添え字1の数が十分に多ければ、1回の操作で満足できると考えられます。位置を構築するために、最初にシーケンス全体のすべての値を何らかの方法でa1に収集し、次にnによって割り当てられたすべての位置について収集できますか?ajの位置にあるすべての値をa1に移動する場合、ajの値はjで割り切れる必要があることに気付きました。次に、ajの値がjを除算できない場合は、他の場所の値をajに追加する必要があります。
a1を使用してajに追加し、ajをjで除算することを考えるのは非常に簡単です。そうすると、次のことがわかります... 2からnまで直接、a2からanをクリアする操作を完了する必要があります。これは正の整数です。これは、最初のn個の数値の合計が少なくともnであることを意味します。つまり、ajの値はjで割り切れる必要があります。
したがって、添え字2からnの最初のパス、2つの操作の各サイクル、最初の操作はajにa1の値を追加して、ajをjで割り切れるようにし、2番目の操作はajのすべての値をa1に移動しますオン。
このような操作のラウンドは2n-2回を消費し、シーケンス全体のすべての値がa1に移動され、次に2からnまでの添え字について、値がa1からコンストラクションに直接移動されます。合計3n-3の操作が消費され、構造を完成させる必要があります。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=1e4+7;
ll num[maxn];
ll n,sum;
int32_t main()
{
IOS;
int t;
cin>>t;
while(t--)
{
sum=0;
cin>>n;
for(ll i=1;i<=n;i++) {
cin>>num[i];sum+=num[i];}
if(sum%n) cout<<-1<<endl;
else
{
sum/=n;
cout<<3*(n-1)<<endl;
for(ll i=2;i<=n;i++)//第一遍for把除了num[1]外的所有值清零,移动到num[1]上
{
ll temp=i-num[i]%i;
if(temp==i) temp=0;
cout<<1<<' '<<i<<' '<<temp<<endl;
num[1]-=temp;
num[i]+=temp;
cout<<i<<' '<<1<<' '<<num[i]/i<<endl;
num[1]+=num[i];
}
for(ll i=2;i<=n;i++) cout<<1<<' '<<i<<' '<<sum<<endl;//第二遍for直接利用num[1]构造
}
}
}
Eタイトル
操作、dfs分割と征服、結論
問題は、自然数のみを含む長さnのシーケンスが与えられた場合、元のシーケンスの各値がXORされ、新しいシーケンスが取得されるように、適切な値を見つける必要があることを意味します。この新しいシーケンスは逆の順序です。数値numが最小で、最小numと対応するout値が出力されます。
ここで、0と1のシーケンスしかないことを前提として、forループのプロセスで0と1が出現する回数を記録し、2つの値に従って、0と1で終了してリバースペアの数を形成できることを理解する必要があります。サイズの違いにより、XORに0と1のどちらを使用するかが決まります。
それ以降、
高いものから低いものへと変化していきます。値に応じて、番号シーケンスは2の深さの累乗に対応するいくつかのセットに分割されます。前のレイヤー番号プロセスは、前の段落の計算プロセスに従って取得できます。その後の影響は下位レベルにのみ影響し、現在のセット間の関係には影響しないため、各セット間の影響はありません。
ここを押した後、dfsの除算と征服を書くだけです。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
int n;
ll ans=0,out=0;//ans为最终的数列中逆序对的数量,out为对原数列中每个值进行异或的值
void dfs(vector<vector<ll>>now,int deep)//二维数组now记录数列在当前层数被分割成了几个部分,deep为当前算的是二进制第几位
{
ll temp=1ll<<deep;
ll cas0,cas1,sum0=0,sum1=0;//sum0和sum1分别记录当前二进制deep位上,以该位为0的值作为逆序对的右侧(也就是out在此位取0)的逆序对数量
//和以该位为1的值作为逆序对的右侧(也就是out在此位取1,使得该位的所有数0和1转化)的逆序对数量
vector<vector<ll>>next;//下一次dfs的二维数组
vector<ll>next0,next1;//当前分割部分的vector中,二进制deep位为0和1的数字分别放入两个vector
for(int i=0;i<now.size();i++)
{
cas0=cas1=0;
for(int j=0;j<now[i].size();j++)
{
if(now[i][j]&temp) {
sum1+=cas0;cas1++;next1.push_back(now[i][j]);}
else {
sum0+=cas1;cas0++;next0.push_back(now[i][j]);}
}
if(next0.size()) {
next.push_back(next0);next0.clear();}//避免不需要的递归和空间浪费,如果next0和next1不为0才压入下次dfs的数组里
if(next1.size()) {
next.push_back(next1);next1.clear();}
}
if(sum0>sum1) {
ans+=sum1;out+=temp;}//如果以该位为0的值作为逆序对右侧的逆序对数量大于以该位为1的值作为逆序对的右侧,代表out需要在该位取1
else ans+=sum0;
if(deep==0) return;
dfs(next,deep-1);
}
int32_t main()
{
IOS;
cin>>n;
vector<vector<ll>>num(1);
num[0].resize(n);
for(auto &x:num[0]) cin>>x;
dfs(num,30);//dfs30层即可,1e9的最高位也不过2的30次
cout<<ans<<' '<<out<<endl;
}