『解題報告』CF1607 (Codeforces Round #753 (Div. 3))


修養身心打水題 Div 3 后面幾題還是有點難度的

比賽鏈接

A. Linear Keyboard

原題鏈接

難度: \(800\) (巨水)

題目大意:
給你一個按鍵打亂只有一行的鍵盤,你可以初始在任意位置,若第 \(i\) 個鍵在位置 \(x_i\) ,下個鍵 \(j\)\(x_j\) 則問你需要 \(|x_i-x_j|\) 的時間來移動 ,輸出打出所給序列的所需時間。

思路: 按照題目模擬即可,時間復雜度\(O(TS)\)

代碼:

#include<bits/stdc++.h>
using namespace std;
const int N=55;
int t;
int str[30];
char ch[N];
inline void Read()
{
    for(int i=1;i<=26;i++)
    {

        char ch=getchar();
        while(!islower(ch)) ch=getchar();
        str[ch-'a']=i;
    }
}
inline void Work()
{
    int ans=0;
    scanf("%s",ch+1);
    for(int i=2;ch[i];i++)
        ans+=abs(str[ch[i]-'a']-str[ch[i-1]-'a']);
    printf("%d\n",ans);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

B. Odd Grasshopper

原題鏈接

難度: \(900\) (沒那么水)

題目大意:
一條無限長的數軸上有一個點,一開始在 \(x_0\) 處。它會移動 \(n\) 次,第 \(i\) 次它會按照以下規則移動:

  • 如果它當前所處的位置上的數 \(x\) 是奇數,則它會移動到 \(x+i\) ,否則它會移動到 \(x-i\)

求移動 \(n\) 次后該點所處的位置上的數。

思路:
按照題目模擬可以發現每過 \(4\) 步都會回到原點,而其他步數與出發點的奇偶有關
設出發點為奇數時與出發點相對位置為\(f_1()\) 偶數時為 \(f_2()\) 可以推出

\[ f_1(x)=\left\{ \begin{aligned} & 0 && n \ mod \ 4 = 0 \\ & -n && n \ mod \ 4 = 1 \\ & 1 && n \ mod \ 4 = 2 \\ & n+1 && n \ mod \ 4=1 \end{aligned} \right. \]

\[\\ f_2(x)=-f_1(x) \]

時間復雜度\(O(T)\)
代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll st,n;
inline void Read()
{
    scanf("%lld%lld",&st,&n);
}
inline void Work()
{
    if(st&1)
    {
        if(n%4==0) printf("%lld\n",st);
        if(n%4==1) printf("%lld\n",st+n);
        if(n%4==2) printf("%lld\n",st-1);
        if(n%4==3) printf("%lld\n",st-n-1);
    }
    else
    {
        if(n%4==0) printf("%lld\n",st);
        if(n%4==1) printf("%lld\n",st-n);
        if(n%4==2) printf("%lld\n",st+1);
        if(n%4==3) printf("%lld\n",st+n+1);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

C. Minimum Extraction

原題鏈接

難度: \(1000\) (似乎比 \(B\) 題還水 ?)

題目大意:
給你一個序列 ,每次可以選擇操作將序列中最小的一個數取出,再將序列剩下的所有數加上它。
再若干次操作后可以使序列中的最小值最大,請求出這個最大的最小值

思路:
排序后按照題目模擬並且動態更新答案即可,時間復雜度\(O(nlogn)\)

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const ll INF=0X3f3f3f3f3f3f3f3f;
int t,n;
int a[N];
ll ans,res;
inline void Read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
}
inline void Work()
{
    ans=-INF,res=0;
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,a[i]-res);
        res=a[i];
    }
    printf("%lld\n",ans);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

D. Blue-Red Permutation

原題鏈接

難度: \(1300\) ()

題目大意:
給你一個序列 ,序列中每個數有紅色藍色兩種 ,你可以將顏色為紅色的數減掉任意值,顏色為藍色的數加上任意值
問若干次操作后可以使序列中的值為 \(1 \sim n\)

思路:
對於紅色的數藍色的數分別從小到大處理,根據貪心思想:當目前需要的值過大時,藍色的數就沒有用了,所以對於一個所需要的數,優先使用藍色的,再用紅色的,如果都不能用了,就無解了。時間復雜度\(O(nlogn)\)

代碼:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n;
int a[N];
multiset<int> blue,red;
inline void Read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    blue.clear(),red.clear();
    for(int i=1;i<=n;i++)
    {
        char ch;
        do ch=getchar();
        while(!isupper(ch));
        if(ch=='B') blue.insert(a[i]);
        else red.insert(a[i]);
    }
}
inline void Work()
{
    for(int i=1;i<=n;i++)
    {
        auto it=blue.begin(),jt=red.begin();
        if(it!=blue.end()&&*it>=i) blue.erase(it);
        else if(jt!=red.end()&&*jt<=i) red.erase(jt);
        else return (void)puts("NO");
    }
    puts("YES");
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

E. Robot on the Board 1

原題鏈接

難度: \(1600\) ()

題目大意:
有一個機器人在 \(n \times m\) 的棋盤上行走(每次只能上下左右移動),給你一個操作序列 \(s\) ,問你機器人從哪開始可以完成最多的操作而不超過邊界。

思路:
由於棋盤是矩形的特性,我們只用管機器人路徑的上下距離極差左右距離極差即可。直接模擬記錄答案並且在不合法時跳出即可。時間復雜度\(O(nm)\)

代碼:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int INF=0X3f3f3f3f;
int t;
int n,m;
char str[N];
int minx,miny,maxx,maxy,ansx,ansy;
inline void Read()
{
    scanf("%d%d",&n,&m);
    scanf("%s",str+1);
}
inline void Work()
{
    int x=0,y=0;
    ansx=ansy=1,minx=maxx=miny=maxy=0;
    for(int i=1;str[i];i++)
    {
        if(str[i]=='U') x--;
        if(str[i]=='D') x++;
        if(str[i]=='L') y--;
        if(str[i]=='R') y++;
        minx=min(minx,x),miny=min(miny,y);
        maxx=max(maxx,x),maxy=max(maxy,y);
        if(maxx-minx>=n||maxy-miny>=m) break;
        ansx=-minx+1,ansy=-miny+1;
    }
    printf("%d %d\n",ansx,ansy);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

F. Robot on the Board 2

原題鏈接

難度: \(2300\) (需要動腦)

題目大意:
還是那個機器人,在 \(n \times m\) 的棋盤上行走(每次只能上下左右移動),每個格子都有一個固定操作,機器人除了不能超過邊界外還不能走到走過的格子上,問從哪出發可以最大化走過的路徑長

思路:
由於每個格子上的操作都是固定的,我們可以得知當我們到了一個格子上時有會有一個固定的結局,掉出邊界 \(or\) 走到環上。
所以我們可以考慮記憶化搜索,記錄每個格子到自己的終點的距離。注意環上的點的答案要全部設定為環長(顯然)。
時間復雜度\(O(nm)\)
代碼:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2010;
int t,n,m;
char str[N][N];
int ansx,ansy,ans;
int d[N][N];
vector<PII> path;
inline bool Check(int x,int y)
{
    return x>0&&y>0&&x<=n&&y<=m;
}
inline void Read()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",str[i]+1);
}
inline void Work()
{
    ans=ansx=ansy=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(d[i][j]>0) continue;//記憶化
            int nowx=i,nowy=j,len=0;
            while(Check(nowx,nowy)&&d[nowx][nowy]==0)//直接進行搜索
            {
                d[nowx][nowy]=--len;
                path.push_back({nowx,nowy});
                if(str[nowx][nowy]=='L') nowy--;
                else if(str[nowx][nowy]=='R') nowy++;
                else if(str[nowx][nowy]=='U') nowx--;
                else if(str[nowx][nowy]=='D') nowx++;
            }
            int cnt=1;
            if(Check(nowx,nowy))//兩種情況:在環上或已經訪問了
            {
                if(d[nowx][nowy]<0)//如果最后是走到環上
                {
                    int circle_len=d[nowx][nowy]-len+1;
                    for(int i=1;i<=circle_len;i++)
                    {
                        auto pos=path.back();
                        path.pop_back();
                        d[pos.first][pos.second]=circle_len;
                    }
                }
                cnt=d[nowx][nowy]+1;
            }
            while(path.size())//處理除環外的路徑
            {
                auto pos=path.back();
                path.pop_back();
                d[pos.first][pos.second]=cnt++;
            }
            if(d[i][j]>ans)
            {
                ans=d[i][j];
                ansx=i,ansy=j;
            }
        }
    printf("%d %d %d\n",ansx,ansy,ans);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            d[i][j]=0;//清空本次的查詢
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

G. Banquet Preparations 1

原題鏈接

難度: \(2200\) (需要動腦)

題目大意:
一共有 \(n\) 道菜,每道菜吃 \(m\) kg 每道菜有 \(a_i\) kg 的魚肉 \(b_i\) kg 的肉,設每道吃 \(x_i\) 的魚肉,試最小化

\[\mid \sum_{i=1}^n(a_i+x_i)-\sum_{i=1}^n(b_i-(m-x_i))\mid \]

思路:
真閱讀理解題
試着化簡式子可得

\[\mid \sum_{i=1}^n(a_i+b_i-m)-2\sum_{i=1}^nx_i\mid \]

對於式子的左半部分是個定值我們記為 \(sum\) ,式子的右邊是一堆范圍的和 ,其中每個 \(x_i\) 滿足

\[max(m- b_i ,0) \leq x_i \leq min(a_i,m) \]

所以我們可以設

\[L \leq \sum_{i=1}^nx_i \leq R \]

接下來分類討論

  • \(sum \leq 2L\) 將所有值取到最小
  • \(sum \leq 2R\) 將所有值取到最大
  • 否則先都取最小值,在逐步考慮,能取大就取大即可(具體見代碼)
    時間復雜度\(O(n)\)
    代碼:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=2e5+10;
int n,m,t;
ll sum,l,r;
int ans[N];
PLL dish[N],range[N];
inline void Read()
{
    scanf("%d%d",&n,&m);
    sum=l=r=0;
    for(int i=1;i<=n;i++)
    {
        static int a,b;
        scanf("%d%d",&a,&b);
        sum+=a-b+m;
        range[i].first=max(m-b,0),range[i].second=min(m,a);
        l+=range[i].first,r+=range[i].second;
    }
}
inline void Work()
{
    if(2*r<=sum)//開到最大
    {
        printf("%lld\n",sum-2*r);
        for(int i=1;i<=n;i++)
            printf("%lld %lld\n",range[i].second,m-range[i].second);
    }
    else if(sum<=2*l)//最小
    {
        printf("%lld\n",2*l-sum);
        for(int i=1;i<=n;i++)
            printf("%lld %lld\n",range[i].first,m-range[i].first);
    }
    else//貪心分配
    {
        printf("%lld\n",sum%2);
        ll base=(sum>>1)-l;
        for(int i=1;i<=n;i++)
        {
            ans[i]=range[i].first;
            int delta=range[i].second-range[i].first;
            if(base>=delta)
                base-=delta,ans[i]+=delta;
            else ans[i]+=base,base=0;
        }
        for(int i=1;i<=n;i++)
            printf("%d %d\n",ans[i],m-ans[i]);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

H. Banquet Preparations 2

原題鏈接

難度: \(2200\) (需要動腦)

題目大意:
一共有 \(n\) 道菜,每道菜吃 \(m_i\) kg,每道菜有 \(a_i\) kg 的魚肉 \(b_i\) kg 的肉,設每道吃 \(x_i\) 的魚肉,試最小化剩下的不同菜

  • 兩道菜 \(i\)\(j\)相同當且僅當兩道菜 \(a_i=a_j \wedge b_i=b_j\)

思路:
我們可以得到兩道菜可能相同必須要滿足 \(a_i+b_i-m_i=a_j+b_j-m_j\) ,所以我們可以按照這個值給所有的菜分類。
分類之后設吃完后的 \(a_i\)\(x_i\)

\[max(0,a_i-m_i) \leq x_i \leq min(0,b_i-m_i) \]

令式子左邊為 \(l_i\) 式子右邊為 \(r_i\)
所以我們把每類的按 \(r_i\) 為第一關鍵字排序,\(l_i\) 為第二關鍵字排序,用單指針貪心維護右端點即可。
時間復雜度\(O(nlogn)\)
代碼:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
int T,n,res;
struct node
{
    int a,b,m;
    int l,r,sum,id;
    inline bool operator<(const node &t) const
    {
        if(sum!=t.sum) return sum<t.sum;
        if(r!=t.r) return r<t.r;
        return l<t.l;
    }
}seg[N];
PII ans[N];
inline void Read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&seg[i].a,&seg[i].b,&seg[i].m);
        seg[i].sum=seg[i].a+seg[i].b-seg[i].m;
        seg[i].l=max(0,seg[i].a-seg[i].m),seg[i].r=seg[i].a+min(seg[i].b-seg[i].m,0);
        seg[i].id=i;
    }
}
inline void Work()
{
    res=0;
    sort(seg+1,seg+n+1);
    int maxr=-1;
    for(int i=1;i<=n;i++)
    {
        if(seg[i].sum!=seg[i-1].sum||seg[i].l>maxr)
            maxr=seg[i].r,res++;
        ans[seg[i].id].first=seg[i].a-maxr;
        ans[seg[i].id].second=seg[i].m-ans[seg[i].id].first;
    }
    printf("%d\n",res);
    for(int i=1;i<=n;i++)
        printf("%d %d\n",ans[i].first,ans[i].second);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        Read();
        Work();
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM