Codeforces Round#660(Div。2)AD問題解決策

Codeforces Round#660(Div。2)AD問題解決策

//評価値2184/2184で書かれている
//黄色のフィールドでは、評価値+152。このゲームにはアルゴリズムはなく、div1プレーヤーを除くdiv2の25位とdiv1プレーヤーの109位を獲得するために、速い手の速度の波に依存しています。それはまぐれですが、黄色の名前を付けられてとても幸せです。
コンテストのリンク:https://codeforces.ml/contest/1388質問
A
シンプルな構造。問題の出題方法はたくさんあるはずですが、ここでは特別な判断方法を採用し、6分で出題される問題の波を原稿に記載しました。
数値nが与えられた場合、a + b + c + d = nを満たす4つの正の整数a、b、c、dを構築する必要があり、a、b、c、dの少なくとも3つの数値はp ×\として表すことができます× q、ここでpとqはどちらも素数です。
まず、2つの素数の乗算として表現できる最小の3つの数値が6、10、および14であることを満足するように簡単に構築できます。これらの3つの数値の合計は30に等しく、4番目の数値は正の整数であるため、n <= 30の場合、ニーズを満たす4つの正の整数を構成できません。
また、nの値が30より大きい場合、6、10、14、n-30の組み合わせを作成できます。
rest = n-30を思い出してから、restが6、10、および14に等しい3つの特殊なケースを判断します。

#include<bits/stdc++.h>
#define ll long long
#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;
        cin>>n;
        if(n<31) cout<<"NO"<<endl;
        else
        {
    
    
            cout<<"YES"<<endl;
            ll rest=n-30;
            if(rest==14)
            {
    
    
                cout<<6<<' '<<9<<' '<<14<<' '<<15<<endl;
            }
            else if(rest==10)
            {
    
    
                cout<<6<<' '<<9<<' '<<10<<' '<<15<<endl;
            }
            else if(rest==6)
            {
    
    
                cout<<5<<' '<<6<<' '<<10<<' '<<15<<endl;
            }
            else cout<<6<<' '<<10<<' '<<14<<' '<<rest<<endl;
        }
    }
}

質問B
単純な構造。
整数nが与えられた場合、長さnの10進数xを作成し、xの各ビットをバイナリに変換して書き換え、この時点でバイナリの最後のnビットを破棄する必要があります。残りのバイナリ値が最大であり、元の数xはできるだけ小さくする必要があります。
まず、貪欲な戦略を採用します。10の数値のバイナリ表現0-9:
0:0
1:1
2:10
3:11
4:100
5:101
6:110
7:111
8:1000
9:1001 をリスト
します。数値8しかないことが簡単にわかります。最後に消去する必要がある長さnが決定されているため、9に対応する2進数は4であり、最後のnビットはヘッダーに影響せず、0が表示されます。したがって、できるだけ長い桁を直接作成することを選択します。つまり、作成する数字の8と9を選択します。
残っている最大のバイナリ値を確保すると同時に、xの初期値ができるだけ小さくなるようにする必要もあります。
最後の2進数のnビットを削除する必要があります。8と9は4ビットに対応します。
元の数値xの末尾のn / 4(切り上げ)桁の場合、8を選択しても9を選択しても違いはありません。最後の残りの値は同じです
。8削除1は100削除2は削除ビットが10の場合、3桁の削除は1、4
桁の削除、それは空9 1桁の削除は100、2桁の削除は10、3桁の削除は
1、4 桁の削除は空なので、n / 4の終わり(上に向かって整数)ビットの場合、小さい方の値8を取り、前に9を追加して、最終的な数値をできるだけ大きくします。

#include<bits/stdc++.h>
#define ll long long
#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;
        cin>>n;
        ll ed=n/4;
        if(n%4) ed++;
        ll f=n-ed;
        for(ll i=0;i<f;i++) cout<<9;
        for(ll i=0;i<ed;i++) cout<<8;
        cout<<endl;
    }
}

質問C
リバース思考、dfs。
n個のノードを持つツリーと、このツリーに住んでいる合計m人の人々を教えてください。
最初はすべてノード1にあり、全員がターゲットノードを持っているため、それぞれ最短の経路をたどります。
彼らが最初に出発したとき、彼らは皆心の状態を持っています。一部の人々は機嫌が良いです、一部の人々は機嫌が悪いです。また、ノード間を移動するとき、機嫌良い人は機嫌が悪くなるかもしれませんが、機嫌が悪い人は常に悪い気分になり、良い気分にはなりません。

各ノードがターゲットノードnum [i]として使用されている人数がわかったら
、各ノードのデータhappy [i]を収集します。データは、上記のプロセスでノードを通過した気分が良い人の数から気分が悪い人の数を引いたものです値。
次に、これらのデータにエラーがあるかどうかを判断する必要があります。

ノード1からそれぞれのターゲットノードではなく、それぞれのターゲットノードからノード1までを逆に考えます。これは、ノード1を開始点とするdfsトラバーサルの再帰プロセスに相当します。
同時に、機嫌が良い人は機嫌が悪くなるかもしれないという対応するは、機嫌が悪い人になると機嫌良くなるかもしれません
現在トラバースされているノードのすべてのサブツリー上のノードは、現在のノードを渡すことです。現在のノードのすべてのサブツリー(現在と表示)の人数の合計と、dfsとnum [now]を再帰的に記録します。現在のノードを通過する合計人数については、sum [now]として記録し、再帰中にすべてのサブツリーで気分が良い人の数を記録し、sumgood [now]として記録します。
(sum [now] -happy [now])/ 2 + happy [now]を使用して、(sum [now] -happy [now])/ 2が整数かどうかを判断するために、現在のノードを通過する気分が良い人の数を計算できます、整数でない場合は、データエラーである必要があります)。機嫌が悪い人は再帰的に戻る過程で機嫌良くなる可能性があるため、現在のノードgoodで機嫌が良い人の数は、すべてのサブツリーsumgood [now]で機嫌が良い人の数より少なくなることはありません。

コーディングしてください。

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=1e5+7;

ll n,m;

struct Edge
{
    
    
    ll to,next;
}edge[maxn*2];

ll head[maxn],tot;

void init()
{
    
    
    for(ll i=1;i<=n;i++) head[i]=-1;
    tot=0;
}

void add(ll u,ll v)
{
    
    
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
//链式前向星存图


bool flag;
ll num[maxn],happy[maxn];
ll sum[maxn],sumgood[maxn];//sum[i]为当前结点与它的所有子节点的总人数,sumgood[i]为当前结点所有子节点的人心情好的人数。

void dfs(ll pre,ll now)//pre为当前结点的递归的前一个结点位置,now为当前结点
{
    
    
    if(!flag) return;
    sum[now]=sumgood[now]=0;
    for(ll i=head[now];i!=-1;i=edge[i].next)
    {
    
    
        ll to=edge[i].to;
        if(to!=pre) //避免反向走回去
        {
    
    
            dfs(now,edge[i].to);
            sum[now]+=sum[to];  //累加所有子树的总人数
            sumgood[now]+=sumgood[to];  //累加所有子树心情好的人的数量
        }
    }
    sum[now]+=num[now]; //加上当前结点的人数,即为经过当前的结点的总人数
    if(happy[now]>sum[now]||(sum[now]-happy[now])%2) flag=0;//如果所有子节点中心情好的人超出了当前结点经过的总人数
    else                                        //或者通过给定数据计算当前结点心情好的人的式子中出现了小数,都代表数据有错
    {
    
    
        ll good=(sum[now]-happy[now])/2+happy[now];//通过给定数据计算当前结点心情好的人的数量
        if(good<sumgood[now]) flag=0;           //该数值不可以小与所有子树心情好的人的数量总和
        else sumgood[now]=good;
    }
}

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        cin>>n>>m;
        for(ll i=1;i<=n;i++) cin>>num[i];
        for(ll i=1;i<=n;i++) cin>>happy[i];
        flag=1;
        init();
        for(ll i=1;i<n;i++)
        {
    
    
            ll u,v;
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        flag=1;
        dfs(-1,1);
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

質問D
貪欲、トポロジー。
長さnの2つの数値a [i]とb [i]が与えられると、最初にans値が0になり、a [i]で数値を選択するたびに、a [i]をansでは、a [i]をa [b [i]]に同時に追加します。
各添え字は1回しか使用できず、一度しか使用できず、最大のans値を見つけて、添字の順序を出力します。

i-> b [i]を有向エッジと見なし、1からnまでのn個の添え字をノードと見なすと、有向グラフに変換されます。(そして、タイトルはすでに閉じたループがないことを示しています)
この有向グラフの各ノードには初期値があり、各ノードは一度取得する必要があります。取得後、現在のノードの値がansに追加されますそして、有向エッジが指す次のノードの値に追加されます。

貪欲な戦略を採用し、ru [i]配列を使用してi番目のノードの度数を記録します。次に、度数が0のノードの値は変更されません。a[i]が負の場合、最終的に最初に取るとa [b [i]]の値が小さくなるため、もう一度取ります。
a [i]が正の場合、すぐにそれを取得し、この値をa [b [i]]に追加して増やします。

要約すると、出力順を保存するために2つのベクトルans1とans2を使用して、有向非循環グラフ全体でトポロジカルソートを直接実行します。
次数が0の添字iの場合、a [i]をansに追加し、a [i]の符号に従って分類および議論します。a
[i]が負の場合、最後にそれも取ります。つまり、その値をa [b [i]]に追加する必要はなく、添え字iがans2に押し込まれます。
現在のノードの値が正の場合、それをすぐに取得します。つまり、その値をa [b [i]]に追加し、下付き文字iをans1に押し込みます。

最終的な出力順序では、
正の添え字はans1に格納され、トポロジーの並べ替え順序に従って直接順序付けできます。これは、これらの正の数の場合、最初の位置を最初に取る方が後者の状況にとってより有益だからです。
Ans2は負の添え字を格納し、それらをトポロジカルソートの逆の順序で出力します。これは、これらの負の数の場合、最初の位置を最初に取ると状況が悪化するためです。

#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=2e5+7;


ll n;
ll a[maxn],b[maxn];
ll ru[maxn];//ru[i]保存结点i的入度

int32_t main()
{
    
    
    IOS;
    cin>>n;
    for(ll i=1;i<=n;i++) cin>>a[i];
    for(ll i=1;i<=n;i++)
    {
    
    
        cin>>b[i];
        if(b[i]!=-1) ru[b[i]]++;
    }
    ll ans=0;
    queue<ll>Q;
    vector<ll>ans1,ans2;
    for(ll i=1;i<=n;i++)
        if(!ru[i]) Q.push(i);
    while(Q.size())///拓扑排序过程
    {
    
    
        ll now=Q.front();
        Q.pop();
        ll to=b[now];
        if(to!=-1)
        {
    
    
            ru[to]--;
            if(!ru[to]) Q.push(to);
            if(a[now]>0) a[to]+=a[now];//如果当前结点的值是正数,那么加到下一个对应结点的值上
        }
        ans+=a[now];//入度为0的结点的值加到ans上
        if(a[now]>=0) ans1.push_back(now);
        else ans2.push_back(now);
    }
    cout<<ans<<endl;
    for(ll i=0;i<ans1.size();i++) cout<<ans1[i]<<' ';//正数结点的下标按照拓扑序输出
    ll len=ans2.size();
    for(ll i=0;i<ans2.size();i++) cout<<ans2[len-i-1]<<' ';//负数结点的下标按照拓扑序的逆序输出
    cout<<endl;
}



おすすめ

転載: blog.csdn.net/StandNotAlone/article/details/107705135