Codeforces Round #696 (Div. 2)(A~D題解)

Codeforces Round #696 (Div. 2)(A~C題解)

——南昌理工學院ACM集訓隊
題目鏈接:https://codeforces.ml/contest/1474

A. Puzzle From the Future

In the 2022 year, Mike found two binary integers a and b of length n (both of them are written only by digits 0 and 1) that can have leading zeroes. In order not to forget them, he wanted to construct integer d in the following way:

he creates an integer c as a result of bitwise summing of a and b without transferring carry, so c may have one or more 2-s. For example, the result of bitwise summing of 0110 and 1101 is 1211 or the sum of 011000 and 011000 is 022000;
after that Mike replaces equal consecutive digits in c by one digit, thus getting d. In the cases above after this operation, 1211 becomes 121 and 022000 becomes 020 (so, d won’t have equal consecutive digits).
Unfortunately, Mike lost integer a before he could calculate d himself. Now, to cheer him up, you want to find any binary integer a of length n such that d will be maximum possible as integer.

Maximum possible as integer means that 102>21, 012<101, 021=21 and so on.

Input
The first line contains a single integer t (1≤t≤1000) — the number of test cases.

The first line of each test case contains the integer n (1≤n≤105) — the length of a and b.

The second line of each test case contains binary integer b of length n. The integer b consists only of digits 0 and 1.

It is guaranteed that the total sum of n over all t test cases doesn’t exceed 105.

Output
For each test case output one binary integer a of length n. Note, that a or b may have leading zeroes but must have the same length n.
在這裏插入圖片描述

題意
現有兩個長度爲n的0,1字符串a,b(其中僅包含0,1),對兩個字符串相加得到字符串c(按數值不進位相加);
例如:
a=“011011”;
b=“101101”;
則c=“112112”;
之後將c中的連續重複數字替換爲一位得到字符串d,對於上述c它的對應字符串d=“1212”;





輸出:題目給出一個字符串b;求一個字符串a使得a+b對應的字符串d最大;

解析:
根據題意可以看出本題利用已知的字符串b來構造字符串a;
由題意已知,字符串d的構造方法,需要去掉c中連續重複的字符,則在構造的過程中應使得c[i]與c[i-1]不相同(字符串長度最大),同時要使c儘可能大;
因爲a與b都是01串,如果b[i]==0,則c[i]==0||c[i]==1,此時a[i]+b[i]最大爲1,看c[i-1]是否爲1,如果c[i-1]==1那麼a[i]不能取1(取1則c[i]==1,會縮短字符串長度)否則取a[i]=1;
若b[i]==1,則c[i]==1||c[i]==2,同理看c[i-1]是否爲2;



#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<unordered_map>
#include<string>
typedef long long ll;
using namespace std;    
 
int main()
{
   
   
    string s;
    int T;
    cin>>T;
    int n;
    while(T--){
   
   
        scanf("%d",&n);
        cin>>s;
        int x=0,y=0;
        for(int i=0;i<n;i++){
   
   
            if(s[i]=='1'){
   
   
                if(s[i-1]=='2'){
   
   
                    cout<<'0';
                }
                else{
   
   
                    cout<<'1';
                    s[i]='2';
                }
            }
            if(s[i]=='0'){
   
   
                if(s[i-1]=='1'){
   
   
                    cout<<'0';
                }
                else{
   
   
                    cout<<'1';
                    s[i]='1';
                }
            }
        }
        cout<<endl;
    } 
    return 0;
}

因爲每一位只與前一位有關係,所以可以邊構造邊輸出;

B. Different Divisors

Positive integer x is called divisor of positive integer y, if y is divisible by x without remainder. For example, 1 is a divisor of 7 and 3 is not divisor of 8.

We gave you an integer d and asked you to find the smallest positive integer a, such that

a has at least 4 divisors;
difference between any two divisors of a is at least d.
Input
The first line contains a single integer t (1≤t≤3000) — the number of test cases.


The first line of each test case contains a single integer d (1≤d≤10000).

Output
For each test case print one integer a — the answer for this test case.

input
2
1
2
output
6
15





題意:
題意比較簡單
給了一個整數d讓我們找出最小的正整數a,使得a至少有4個因子a的任意兩個因數之差至少爲d。

解析:
1:對於一些素數p和q,整數有4個約數,如果它的形式是p×q或p3。在第一種情況下,它有1,p, q, pq。
在第二種情況下,它有因子1 p p2 p3。
2:根據貪心,本題不是找到至少有4個約數的整數,而是找到恰好有4個約數的整數(任意兩個因數相差大於等於d)。
3:若p爲a最小的質因數,要使得任意兩個因數只差至少爲d則p≥d+1。
則解法爲:
四個因數,第一個爲1,第四個爲a,則中間兩個就爲第一個比1大於等於d的質數和第一個比第二個因數大於等於的質數,將它們相乘即可得到 a
即:1,d+m,d+m+n,(d+m)





(d+d+m)(m爲找不到恰好相差d的時候找到的第一個大於d的質數-d,n同理)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<unordered_map>
#include<string>
typedef long long ll;
using namespace std;    
const int N=200005;

bool vis[N];
ll primer[N];//存素數
int cnt=0;
void find_primer(){
   
   //歐拉篩
    for(ll i=2;i<=N;i++){
   
   
        if(!vis[i])primer[cnt++]=i;
        
        for(ll j=0;j<cnt&&primer[j]*i<=N;j++){
   
   
            vis[i*primer[j]]=1;

            if(i%primer[j]==0)break;
        }
    }
}

int main()
{
   
   
    string s,c;
    int T;
    cin>>T;
    int n;
    find_primer();
    // for(int i=0;i<=10000;i++){
   
   
    //     cout<<primer[i]<<endl;
    // }
    while(T--){
   
   
        scanf("%d",&n);//差值n
        ll ans=0;
        ll a=upper_bound(primer,primer+cnt,n)-primer;//二分查找找到第一個差值爲n的質數;
        a=primer[a];
        ll b=lower_bound(primer,primer+cnt,a+n)-primer;//二分查找找到第二個差值爲n的質數
        b=primer[b];
        ans=a*b;//相乘求得ans的值
        cout<<ans<<endl;
    } 
    return 0;
}

C. Array Destruction

You found a useless array a of 2n positive integers. You have realized that you actually don’t need this array, so you decided to throw out all elements of a.

It could have been an easy task, but it turned out that you should follow some rules:

In the beginning, you select any positive integer x.
Then you do the following operation n times:
select two elements of array with sum equals x;
remove them from a and replace x with maximum of that two numbers.
For example, if initially a=[3,5,1,2], you can select x=6. Then you can select the second and the third elements of a with sum 5+1=6 and throw them out. After this operation, x equals 5 and there are two elements in array: 3 and 2. You can throw them out on the next operation.



Note, that you choose x before the start and can’t change it as you want between the operations.

Determine how should you behave to throw out all elements of a.

Input
The first line contains a single integer t (1≤t≤1000) — the number of test cases.

The first line of each test case contains the single integer n (1≤n≤1000).

The second line of each test case contains 2n integers a1,a2,…,a2n (1≤ai≤106) — the initial array a.

It is guaranteed that the total sum of n over all test cases doesn’t exceed 1000.

Output
For each test case in the first line print YES if it is possible to throw out all elements of the array and NO otherwise.

If it is possible to throw out all elements, print the initial value of x you’ve chosen. Print description of n operations next. For each operation, print the pair of integers you remove.
在這裏插入圖片描述
題意:
給出一個長度爲2*n的數組;
一開始,你選擇任意正整數x。
然後做下面的操作n次:
選擇數組中的兩個元素u,v,使得u+v = x;
然後從a中移除u與v,並用這兩個數的最大值替換x。
對於每個測試用例,如果可以拋出數組中的所有元素,則打印YES,否則打印NO。
如果可以拋出所有元素,則打印所選擇的x的初始值。接下來打印n個操作的描述。對於每個操作,打印要刪除的一對整數u,v。








例如,如果初始a=[3,5,1,2],則可以選擇x=6。然後你可以用sum 5+1=6選擇a的第二個和第三個元素並把它們扔出去。在這個操作之後,x等於5,數組中有兩個元素:3和2。下次手術的時候你就可以扔了。

解析:
由題意可知,更新的x爲拋棄的u,v的最大值(且u+v==x才能拋棄,a[i]不爲負數),則無論第一個x取值爲多少,第一輪拋棄中必定有a中最大的數字max(a),否則在之後的拋棄中max(a)永遠無法拋棄,同理第二輪拋棄中必定有a中第二大的數字,第三輪拋棄中必定有a中第三大的數字……

我開始的想法是,先將數組排序,for循環從後往前遍歷,先以最大值爲x查找每一輪的u,v,之後用vis數組標記,之後剩餘的那個數字與最大值匹配得到初始的x0,但是這個想法樣例都過不了;
最後一個樣例:
5
1 2 3 4 5 6 7 14 3 11
排序後
1 2 3 3 4 5 6 7 11 14
對這個數組進行上述操作:
x=14->u1=11,v1=3->x=11
x=11->u2=7,v2=4->x=7(這裏就開始不對勁了QAQ)
x=7->u3=6,v3=1->x=6
x=6->無法匹配,NO
很顯然這個貪心是錯誤的;還是得先求出初始的x0










仔細讀題發現“It is guaranteed that the total sum of n over all test cases doesn’t exceed 1000.”(確保所有測試用例中n的總和不超過1000。)哦闊ヾ(≧▽≦)o*

直接先把初始的x0求出來,那麼如何找到初始的x0?枚舉!
枚舉每一個數與最大的數相匹配,將這兩個數拋棄,之後的操作就和上面的貪心一樣

#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<unordered_map>
#include<string>
typedef long long ll;
using namespace std;    
// const int N=200005;
 
struct lq{
   
   
    int x,y;
}f[2005];
int v[2005];//記憶化數組
int a[2005];
int n;
 
int find(int p){
   
   //查找與p相匹配的值
    int ans=lower_bound(a+1,a+n+1,p)-a;//二分查找
    while(v[ans])ans++;
    if(a[ans]==p)return ans;
    else return -1;
}
 
int main()
{
   
   
    int T;
    cin>>T;
    while(T--){
   
   
        scanf("%d",&n);
        n*=2;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+n+1);//排序
        // for(int i=1;i<=n;i++)cout<<a[i]<<' ';
        // cout<<endl;
        int flag=0;
        int cnt=0;
        for(int k=1;k<n;k++){
   
   
            memset(v,0,sizeof(v));//清空
            int x=a[n];
            cnt=0;
            f[++cnt].x=a[k];f[cnt].y=x;//存u,v
            v[n]=1;v[k]=1;//標記
            flag=0;
            for(int i=n-1;i>=1;i--){
   
   
                if(v[i]==0){
   
   
                    v[i]=1;
                    int ans=find(x-a[i]);//查找相匹配的值,返回-1爲沒有找到
                    if(ans!=-1){
   
   
                        f[++cnt].x=a[ans];
                        f[cnt].y=a[i];
                        x=max(a[i],a[ans]);
                        v[i]=1;v[ans]=1;
                    }
                    else{
   
   //沒找到直接下一輪查找
                        flag=1;break;
                    }
                }
            }
            if(flag==0){
   
   //全部找到了,就跳出
                break;
            }
        }
        if(flag==1){
   
   
            printf("NO\n");
            continue;
        }
        printf("YES\n");
        printf("%d\n",f[1].x+f[1].y);
        for(int i=1;i<=cnt;i++){
   
   
            printf("%d %d\n",f[i].x,f[i].y);
        }
    } 
    return 0;
}

小蔡姬實力有限,只會這三題,見諒(‾◡◝)

科普一下這個lower_bound( )

lower_bound( )upper_bound( )都是利用二分查找的方法,左閉右開有序區間裏進行二分查找的。
兩個函數在algorithm頭文件下;

lower_bound( begin,end,num):從數組的begin位置到end-1位置二分查找第一個大於或等於num的數字;

upper_bound( begin,end,num):從數組的begin位置到end-1位置二分查找第一個大於num的數字;

找到返回該數字的地址,不存在則返回end。

來補個D題,剛剛看完大佬的博客

大佬的博客(原文)

D. Cleaning

During cleaning the coast, Alice found n piles of stones. The i-th pile has ai stones.

Piles i and i+1 are neighbouring for all 1≤i≤n−1. If pile i becomes empty, piles i−1 and i+1 doesn’t become neighbouring.

Alice is too lazy to remove these stones, so she asked you to take this duty. She allowed you to do only the following operation:

Select two neighboring piles and, if both of them are not empty, remove one stone from each of them.
Alice understands that sometimes it’s impossible to remove all stones with the given operation, so she allowed you to use the following superability:

Before the start of cleaning, you can select two neighboring piles and swap them.
Determine, if it is possible to remove all stones using the superability not more than once.

Input
The first line contains a single integer t (1≤t≤104) — the number of test cases.

The first line of each test case contains the single integer n (2≤n≤2⋅105) — the number of piles.

The second line of each test case contains n integers a1,a2,…,an (1≤ai≤109) — the number of stones in each pile.

It is guaranteed that the total sum of n over all test cases doesn’t exceed 2⋅105.

Output
For each test case, print YES or NO — is it possible to remove all stones using the superability not more than once or not.

在這裏插入圖片描述
題意:
給出n堆石頭,第i堆有ai個石頭,現在需要清空這些石頭,可執行的操作爲:
1、選擇兩個相鄰的堆,如果它們都不是空的,從每個堆中移出一塊石頭。
2、在開始清理前,可以選擇相鄰的兩堆並進行交換。(僅限一次)
現在請問能否將這n堆石頭清空
對於所有1≤i≤n−1的堆i和i+1是相鄰的。如果第i堆變成空的,第i - 1堆和第i+1堆就不會相鄰。
能清空輸出YES,否則輸出NO






解析:
題目給了兩種操作,我們首先考慮一個子問題沒有操作2的時候怎樣判斷是否能清空數組;

首先發現一點,左右端點的石子能否清零僅由它們後面的那一堆石子的數量決定,即端點值數應小於等於它們後面的值,否則必定爲NO
a2>=a1時,便可清空a1,此時a2=a2-a1,a2變爲端點值,如此便可以從左往右不斷更新端點值;
同理也可從左往右不斷更新右邊的端點值
用一個前綴數組和一個後綴數組分別去記錄中間的過程;


注:若是中途出現pre[i-1]>a[i]的,那麼此時意味着如果將這兩個相鄰的減了,就將導致a[i-1]變成空,不能滿足題目的要求,對於這種情況,先定義兩個不同的較大值(比a[i]範圍大),記錄pre[i]爲其中一個較大值;同理suf遇到這種情況則賦值爲另一個較大值(在交換相鄰項的時候需要判前後綴是否相等,兩個較大值不能相同)

求出兩個數組:pre[i]=a[i]-pre[i-1] ;suf[i]=a[i]-suf[i+1] ;

假如說有解的話,那麼一定有一個分界點i使得他倆合併起來之後有什麼相等條件表示,使得左右兩邊都可以刪除完,很顯然這個條件爲pre[i]==suf[i+1]

簡單的講pre[i]表示從左往右消剩餘的石子,suf[i+1]表示從右往左消剩餘的石子
當pre[i]==suf[i+1]的時候就相當於只剩兩堆石子,且兩堆石子數量相等(這不就一下就消掉了嗎)

上述方法是不考慮操作2的情況,那麼當我們考慮操作2

即爲當**pre[i]!=suf[i+1]**我們可以將a[i]與a[i+1]的值交換再進行一次判斷;

總結:先構造出上述的前後綴數組pre和suf,之後從一個循環左往右枚舉pre[i]的值和suf[i+1]的值是否相等,相等即爲YES跳出循環;若是不相等,則交換a[i]與a[i+1]①(下解釋交換)然後判斷,若是相等則爲YES後跳出,否則進入下一重循環。

①如何交換a[i]與a[i+1]:當不匹配的時候交換然後重新求pre和suf數組?當然不可能,限時2 seconds都不允許你這麼操作,此時:
已知:pre[i]=a[i]-pre[i-1];
suf[i+1]=a[i+1]-suf[i+2];
那麼交換a後的pre[i]和suf[i+1]是不是就一目瞭然
交換後pre[i]=a[i+1]-suf[i+2] ; suf[i+1]=a[i+1]-pre[i-1] ;(注意這裏要判斷一下是否爲負數)



#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<unordered_map>
#include<string>
typedef long long ll;
using namespace std;    

const ll INF=1e18;
const int N=2e5+5;
ll a[N];
ll pre[N];
ll suf[N];

int main()
{
   
   
    cin.tie(0);std::ios::sync_with_stdio(false);
    int T;
    cin>>T;
    int n;
    while(T--){
   
   
        cin>>n;
        memset(pre,0,sizeof(pre));//清空前後綴數組
        memset(suf,0,sizeof(suf));
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++){
   
   //構造前綴數組
            if(a[i]>=pre[i-1])pre[i]=a[i]-pre[i-1];
            else pre[i]=INF-1;
        }
        for(int i=n;i>=1;i--){
   
   //構造後綴數組
            if(a[i]>=suf[i+1])suf[i]=a[i]-suf[i+1];
            else suf[i]=INF;
        }
        int flag=0;
        for(int i=1;i<n;i++){
   
   //枚舉判斷
            if(pre[i]==suf[i+1]){
   
   
                flag=1;
                break;
            }
            else {
   
   //不相等則交換判斷
                ll ans=a[i];
                ll cnt=a[i+1];
                if(ans>=suf[i+2]&&cnt>=pre[i-1]){
   
   
                    ans-=suf[i+2],cnt-=pre[i-1];
                    if(ans==cnt){
   
   
                        flag=1;
                        break;
                    }
                }
            }
        }
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    } 
    return 0;
}

扯完了,掰掰!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章