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

Codeforces Round#675(Div。2)AD問題解決策
コンテストリンク:https://codeforces.com/contest/1422

質問A

質問は、四辺形の3つの辺が与えられた場合、四辺形を形成する代わりに整数を出力できることを意味します。

三角形の定理と同様に、最小の長さを満たす必要がある3つの辺の合計は、最大の辺よりも大きくする必要があります。
ここで選択した構築方法は、最大の辺から他の2つの辺を減算し(最大値を取得するには0)、1を加算することです。

#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 a,b,c;
        cin>>a>>b>>c;
        if(a>b) swap(a,b);
        if(b>c) swap(b,c);
        cout<<max(0ll,c-a-b)+1<<endl;
    }
}

質問B
簡単な結論、詳細な処理。

タイトルは、n ×\ timesが与えられたことを意味します× n行列の場合、各操作で特定の数に+1または-1を指定し、行列全体の各列を対称にするために少なくとも必要な操作の数を尋ねることができます。各行も対称です。

ここでは、最初に、マトリックスの中心を原点として使用し、水平方向と垂直方向に座標系を確立します。マトリックス内のx軸とy軸上の点は、座標軸上ではなく、別の数値と一致する必要があるだけであることがわかります。すべてのポイントは、他の3つの数値と一致している必要があります。

2つの数値を同じに保つための操作の数は単純で、2つの数値の差の絶対値です。
4つの数値が一致している場合、最終的な数値の値がどの範囲内にあるかを仮定して結論を​​出すことができます。
4つの数値がa、b、c、dのように小さいものから大きいものへとソートされているとします。最後に作成した値がcの右側にあると仮定すると、この値を-1にすることができます。この場合、a、b、cの3つの数値は、1回少なく操作され、dはもう一度操作され、操作の総数は-2になります。 、 良くなります。
同様に、bの左側の最終的な構築値も最適ではないと推測できます。
したがって、作成した最終値はbからcまでの閉区間にあり、操作の数は同じです。したがって、cから4つの数値の絶対値を直接差し引くことが、操作の最小数です。

それからそれは達成されました。

#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;

ll num[107][107];

int32_t main()
{
    
    
    IOS;
    int t;
    cin>>t;
    while(t--)
    {
    
    
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) cin>>num[i][j];
        ll ans=0;
        for(int i=1;i<=n/2;i++)//除了奇数行的中间行外,所有位置都有对应三个另外的位置要保持相同
        {
    
    
            for(int j=1;j<=m/2;j++)
            {
    
    
                vector<ll>temp;
                temp.push_back(num[i][j]);
                temp.push_back(num[i][m-j+1]);//同一行的另一个数
                temp.push_back(num[n-i+1][j]);//同一列的另一个数
                temp.push_back(num[n-i+1][m-j+1]);//关于矩阵中心对称的另一个数
                sort(temp.begin(),temp.end());
                for(auto &x:temp) ans+=abs(x-temp[2]);
            }
            if(m&1) ans+=abs(num[i][m/2+1]-num[n-i+1][m/2+1]);//中间列是特殊情况
        }
        if(n&1)//奇数行的中间那一行额外处理,此时每个位置需要保持相同的数只有另外一个
        {
    
    
            for(int j=1;j<=m/2;j++) ans+=abs(num[n/2+1][j]-num[n/2+1][m-j+1]);
        }
        cout<<ans<<endl;
    }
}

Cタイトル
dp

この質問は、最大長が1e5レベルの番号が与えられた場合、0以外の長さの番号の連続セグメントを削除し、残りの番号を結合して新しい番号を形成できることを意味します。
削除スキームからのすべての数値の合計を出力する必要があります。

見やすい線形dpです。具体的な伝達方程式については、コードとコメントを参照してください。

#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=1e5+7;
const ll mod=1e9+7;

string s;

struct Node
{
    
    
    ll data,cas;//data记录值总共为多少,cas为有多少组情况
};

Node dp[maxn][3];//方便理解起见,这里每个下标对应三种状态
//dp[i][0]代表用前i个数字在已经删除了一段区间并且这个区间的最后一个位置是下标i的情况下,构成的所有数值和,以及对应有多少组情况
//dp[i][1]代表用前i个数字在已经删除了一段区间并且这个区间的最后一个位置不是下标i的情况下,构成的所有数值和,以及对应有多少组情况
//dp[i][2]代表用前i个数字未删除区间的情况下,构成的所有数值和,以及对应有多少组情况(这里一直是1)

int32_t main()
{
    
    
    IOS;
    cin>>s;
    dp[0][2].cas=1;
    for(int i=0;i<s.size();i++)
    {
    
    
        dp[i+1][0].cas=(dp[i][0].cas+dp[i][2].cas)%mod;//删除当前位置,由于只能删除一段连续的区间,因此不能从dp[i][1]转移过来
        dp[i+1][0].data=(dp[i][0].data+dp[i][2].data)%mod;

        dp[i+1][1].cas=(dp[i][0].cas+dp[i][1].cas)%mod;//保留当前位置,并且之前已经有删除过,因此从dp[i][0]和[1]转移过来
        dp[i+1][1].data=((dp[i][0].data+dp[i][1].data)%mod*10%mod+dp[i+1][1].cas*(s[i]-'0')%mod)%mod;

        dp[i+1][2].cas=dp[i][2].cas;
        dp[i+1][2].data=(dp[i][2].data*10+s[i]-'0')%mod;
    }
    cout<<(dp[s.size()][0].data+dp[s.size()][1].data)%mod<<endl;
}

質問D
思考、結論の最適化、最短パス

問題は、2次元平面上の開始点と終了点の座標が与えられた場合、移動するたびに上下左右に1の距離しか移動できないことを意味します。
ただし、この平面にはm個の特殊ノードがあります。位置がこのノードの横または縦と同じである場合、時間をかけずにこのノードの位置に直接到達できます。少なくとも開始点から終了点に移動する回数を尋ねます(特別なノードへのテレポートの移動を除く)。

ここで、始点と終点、およびm個の特殊点がグラフのノードと見なされ、始点と終点の間の距離が横軸の差の絶対値であり、特殊点がある場合の最短経路問題と見なされることは容易に想像できます。終点として、横軸と縦軸の差の小さい方の値を距離として使用します。ただし、このような構成の場合は、m + 2ノードの完全なグラフであり、エッジの数(m + 2)2が最短パスを実行します。
始点からm個の特殊点までの一方向エッジと、始点とm個の特殊点から終点までの一方向エッジをグラフに追加する必要があることに気づきました。
ただし、m個の特別なポイント間のm 2個の特別なエッジは引き続き最適化でき、すべてを追加する必要はありません。
ここでは、最初に小さな結論を出す必要があります。2つのポイントa [x1、y1]、b [x2、y2]、x1 <x2、y1 <y2について、3番目のポイントc [x3、y3]がある場合は、x1 <を満たします。 = x3 <= x3またはy1 <= y2 <= y3。
ポイントaからポイントbまでは、ポイントaからx方向(水平)またはy方向(垂直)を経由して、bに直接移動できる位置に移動する必要があります。このプロセスでは、必然的に、ポイントcの水平座標と垂直座標が同じである特定のポイントを通過し、ポイントcにテレポートしてから、元のパス方向をたどります。必要な移動回数は変わりません。つまり、aとbの間のパスは、aからcへのパスとcからbへのパスの2つで構成できます。
つまり、任意の2つの特別なポイント間の距離は、x方向またはy方向の2つの特別なポイント間のポイントのエッジで表すことができます。この結論を使用して大きなエッジを連続的に分割した後、それらは最終的にx方向とy方向の2つの隣接するポイント間のエッジに変換されます。
その結果、M 2つのエッジは2M-2エッジに圧縮される。このとき、最短経路アルゴリズムmlognは時間で実行することができます。

#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 maxm=1e5+7;

struct Edge
{
    
    
    ll to,next,dis;
}edge[maxm<<3];

ll head[maxm],tot;
ll n,m;

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

void add(ll u,ll v,ll w)
{
    
    
    edge[tot].to=v;
    edge[tot].next=head[u];
    edge[tot].dis=w;
    head[u]=tot++;
}

ll dis[maxm];

struct Node
{
    
    
    ll pos,val;
    Node(ll pos,ll val):pos(pos),val(val){
    
    }
    friend bool operator < (Node a,Node b)
    {
    
    
        return a.val>b.val;
    }
};

ll Dijstra()
{
    
    
    for(ll i=0;i<=m+1;i++) dis[i]=llINF;
    dis[0]=0;
    priority_queue<Node>Q;
    Q.push(Node(0,0));
    while(Q.size())
    {
    
    
        Node now=Q.top();
        Q.pop();
        if(now.val>dis[now.pos]) continue;
        for(ll i=head[now.pos];i!=-1;i=edge[i].next)
        {
    
    
            ll to=edge[i].to;
            if(dis[to]>edge[i].dis+now.val)
            {
    
    
                dis[to]=edge[i].dis+now.val;
                Q.push(Node(to,dis[to]));
            }
        }
    }
    return dis[m+1];
}

struct Point
{
    
    
    ll x,y,tar;
}point[maxm];

bool cmp1(Point a,Point b){
    
    return a.x<b.x;}
bool cmp2(Point a,Point b){
    
    return a.y<b.y;}

int32_t main()
{
    
    
    IOS;
    cin>>n>>m;
    init();
    cin>>point[0].x>>point[0].y>>point[m+1].x>>point[m+1].y;//起点下标0,终点下标m+1
    for(ll i=1;i<=m;i++) {
    
    cin>>point[i].x>>point[i].y;point[i].tar=i;}
    for(ll i=1;i<=m;i++) add(0,i,min(abs(point[i].x-point[0].x),abs(point[i].y-point[0].y)));//起点到m个特殊点的边加进图中
    for(ll i=0;i<=m;i++) add(i,m+1,abs(point[m+1].x-point[i].x)+abs(point[m+1].y-point[i].y));//起点和m个特殊点,与终点的边加进图中
    //以上均可以直接设置为单向边
    //之后还需要加的就是m个特殊点之间的边了
    sort(point+1,point+m+1,cmp1);//按照x从小到大排序后,相邻特殊点之间建立双向边
    for(ll i=1;i<m;i++)
    {
    
    
        ll dis=min(abs(point[i].x-point[i+1].x),abs(point[i].y-point[i+1].y));
        add(point[i].tar,point[i+1].tar,dis);
        add(point[i+1].tar,point[i].tar,dis);
    }
    sort(point+1,point+m+1,cmp2);//按照y从小到大排序后,相邻特殊点之间建立双向边
    for(ll i=1;i<m;i++)
    {
    
    
        ll dis=min(abs(point[i].x-point[i+1].x),abs(point[i].y-point[i+1].y));
        add(point[i].tar,point[i+1].tar,dis);
        add(point[i+1].tar,point[i].tar,dis);
    }
    cout<<Dijstra()<<endl;//跑个最短路完事了
}

おすすめ

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