1. 程式人生 > 實用技巧 >7.18 2020牛客暑期多校訓練營(第三場)題解及補題

7.18 2020牛客暑期多校訓練營(第三場)題解及補題

目錄

7.18 2020牛客暑期多校訓練營(第三場)題解及補題

比賽過程

D題,B題,A題,C題。

D題簽到。AB同時開的,B其實可以看成環思路,只不過一開始str+=str,太費時間了,改了這立馬過;A題一開始愣是沒看懂題意,其實真正需要分析的是情況1;C題最後分析的是長度為9的邊和長度為8的邊,其實想想就是題面讓你看的噁心,就是(用我們的解法)兩個帶60°角的三角形是正的還是反的。

題解

A Clam and Fish

題意

有一個捕魚遊戲,共有n個池塘,編號1-n,每個池塘有四種狀態,分別為0,1,2,3.
0代表:沒有魚也沒有蛤
1代表:沒有魚,有一條蛤
2代表:有一條魚,沒有蛤
3代表:有一條魚和一條蛤
對於每一個池塘,我們有四種選擇:
1.如果池塘裡有蛤,可以用這個蛤製作一包魚餌,所擁有的魚餌包裝數將增加一。在此操作之後,您可以使用此包魚餌捕獲魚。
2.如果池塘裡只有一條魚,則無需任何魚餌就可以抓到這條魚。在此操作之後,擁有的魚餌包數不會改變。
3.如果有至少一包魚餌。即使在此階段沒有魚,也可以使用一包魚餌捕獲一條魚。在此操作之後,擁有的魚餌包數將減少一。
4.什麼也不做

解法

本題為貪心,我們的策略為,如果池塘為1,那麼製作魚餌;如果池塘為0並且已經有了魚餌,使用魚餌捕一條魚;如果池塘為2或者3,
補一條魚,由於最後魚餌可能會有剩餘的,所以最後答案要加上cnt/2,也就是1號池塘有一半是需要製作魚餌,另一半是來捕魚。

程式碼

#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = ~0u >> 1;
typedef pair<int,int> P;
#define REP(i, a, n) for (int i = a; i < (n); ++i)
#define PER(i, a, n) for (int i = (n) - 1; i >= a; --i)
int main() {
    IO;
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        string s;
        cin >> s;
        int cnt = 0;
        int ans = 0;
        REP(i, 0, n) {
            if (s[i] == '1') {
                cnt++;
            }
            else if (s[i] == '0' && cnt > 0) {
                ans++;
                cnt--;
            }
            else if (s[i] == '3' || s[i] == '2') {
                ans++;
            }
        }
        cout << ans + cnt / 2 << endl;
    }
    return 0;
}

B Classical String Problem

題意

給一個由小寫字母組成的字串,以及Q個操作,操作有兩種型別,操作M表示修改字串:給定整數x,需要根據x的值修改S。
如果x為正,則將S中最左邊的x個字母移到S的右邊;否則,移動最右邊的|x|長度到S左側;操作A表示詢問字元:給定正整數x。
輸出當前字串S中的第x個字母是什麼。

解法

該題題意比較好理解,但是要注意超時的問題,由於M操作比較費時,我們不可以模擬每一個操作,我們需要記錄每一次操作S
字元的移動距離,當詢問字元時,只需要O(1)輸出對應位置的字元即可,剩下的就是取模的細節了。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int maxn = 2000500;
const ll mod = 1e9 + 7;
typedef pair<int,int> pii;
const double pi = acos(-1);
int main() {
    IO;
    string str;
    cin>>str;
    int T;
    cin>>T;
    int cnt=0;
    int len=str.length();
    while(T--) {
        char ch;
        int x;
        cin>>ch>>x;
        if(ch=='M') {
            if(x>0) {
                cnt+=x;
                cnt%=(len);
            }
            else {
                cnt+=x;
                if(cnt<0) {
                    cnt+=(len);
                }
            }
        }
        else {
            cout<<str[(cnt+x-1)%len]<<endl;
        }   
    }  
    return 0;

}

C Operation Love

題意

t組測試,每次測試按順時針或逆時針給20個點,是一個手的形狀,問是左手還是右手。

解法

這裡放的是訓練時的比較“野”的一種做法:

題圖如上,因為左手和右手是鏡面對稱的,所以不同點是很容易找到的;另外,給定的點是順時針或者逆時針給出的,所以要是想根據點的輸入判斷左手還是右手是不可能的。可以選擇凸包什麼的,但是當時看到題面裡面提到,手的形狀是不會放大或者縮小的,但是可以旋轉和平移,平移的話,所有的點加或者是減都是一樣的形狀。

所以選定了(1,0),(10,0),(10,8)相對應的三個點,將(10,0)的座標置為0,判斷其他兩個點的象限即可。

方法比較“野”,但是不失為一種方法。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int maxn = 2000500;
const ll mod = 1e9 + 7;
typedef pair<int,int> pii;
const double pi = acos(-1);
struct node
{
    double x,y;
}a[25],b,c,d;
double cal(node a,node b) {
    return sqrt((a.x-b.x)*((a.x-b.x))+(a.y-b.y)*(a.y-b.y));
}
int main() {
    IO;
    int T;
    cin>>T;
    while(T--) {
        cin>>a[1].x>>a[1].y;
        for(int i=2;i<=20;i++) {
            cin>>a[i].x>>a[i].y;
        }
        bool flag=true;
        a[0].x=a[20].x;
        a[0].y=a[20].y;
        a[21].x=a[1].x;
        a[21].y=a[1].y;
        for(int i=1;i<=20;i++) {
            if(cal(a[i],a[i-1])>9.00-1e-1&&cal(a[i],a[i-1])<9.00+1e-1 && cal(a[i],a[i+1])>8.00-1e-1&&cal(a[i],a[i+1])<8.00+1e-1) {
                c.x=a[i].x;
                c.y=a[i].y;
                b.x=a[i+1].x-c.x;
                b.y=a[i+1].y-c.y;
                d.x=a[i-1].x-c.x;
                d.y=a[i-1].y-c.y;
                flag=false;
                break;
            }
            else if(cal(a[i],a[i+1])>9.00-1e-1&&cal(a[i],a[i+1])<9.00+1e-1 && cal(a[i],a[i-1])>8.00-1e-1&&cal(a[i],a[i-1])<8.00+1e-1) {
                c.x=a[i].x;
                c.y=a[i].y;
                b.x=a[i-1].x-c.x;
                b.y=a[i-1].y-c.y;
                d.x=a[i+1].x-c.x;
                d.y=a[i+1].y-c.y;
                flag=false;
                break;
            }
        }
        if(b.y==0) {
            if(b.x>0&&d.y>0) cout<<"right\n";
            else if(b.x>0&&d.y>0) cout<<"left\n";
            else if(b.x<0&&d.y>0) cout<<"left\n";
            else cout<<"right\n";
        }
        else if(b.x==0) {
            if(b.y>0&&d.x>0) cout<<"left\n";
            else if(b.y<0&&d.x>0) cout<<"right\n";
            else if(b.y>0&&d.x<0) cout<<"right\n";
            else cout<<"left\n";
        }
        else if(b.x>0&&b.y>0) {
            if(d.x>0) cout<<"left\n";
            else cout<<"right\n";
        }
        else if(b.x<0&&b.y>0) {
            if(d.x>0) cout<<"left\n";
            else cout<<"right\n";
        }
        else if(b.x<0&&b.y<0) {
            if(d.x<0) cout<<"left\n";
            else cout<<"right\n";
        }
        else {
            if(d.x<0) cout<<"left\n";
            else cout<<"right\n";
        }

    }
     
    return 0;

}

D Points Construction Problem

題意

在2D平面內,每個格點(整數點)有一個白點,可以將其中一些點塗黑。問能否將n個白點塗黑,使得有m對相鄰的白點和黑點(指哈夫曼距離為1)

解法

借鑑一位大佬的比賽時的思路,感覺好理解

①若每個黑點都是不相鄰的,則可以發現塗黑n個的黑白對數上限為\(4*n\)

②如果每個黑點相鄰,並排成一行,則此時黑白對數為\(2*n+2\)

③可以發現黑白點對數一定是偶數,在②的基礎上,將鏈上的黑點逐個拿出並放置在互不相鄰處,則可以實現\(2∗n+2\)至$ 4∗n$所有偶數m的構造。

④如果m比\(2∗n+2\)還要小,可以在②的基礎上,將這條黑點鏈逐步捲曲來減少黑白點對數,每次減2

程式碼

#include <bits/stdc++.h>
using namespace std;
int vis[100][100];
int cal(int u,int v){//周圍黑點的個數
    return vis[u-1][v]+vis[u+1][v]+vis[u][v+1]+vis[u][v-1];
}
int main () {
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        if(n*4<m || m%2==1){
            puts("No");
            continue;
        }
        int srt=sqrt(n);
        int rest=n-srt*srt;
        int down=srt*4;
        if(rest>0){
            down+=2;
        }
        if(rest>srt){
            down+=2;
        }
        if(m<down){
            puts("No");
            continue;
        }
        puts("Yes");
        if((n*4-m)/2<n){//單鏈
            int num2=(n*4-m)/2;
            int num4=n-num2;
            for(int i=1;i<=num2+1;i++){
                printf("%d %d\n",i,1);
            }
            num4--;
            for(int i=1;i<=num4;i++){
                printf("%d %d\n",-i,-i);
            }
        }
        else{//進行卷
            memset(vis,0,sizeof vis);
            int num0=(2*n+2-m)/2;
            int cnt=0;
            vis[50][50]=1;
            cnt++;
            printf("50 50\n");
            int x=50,y=50;
            int turn=0;//0右,1上,2左,3下
            for(int step=1;step<=10;step++){
                for(int sstep=1;sstep<=2;sstep++){//同一步長走兩次
                    if(turn==0){
                        for(int i=1;i<=step;i++){
                            x++;
                            vis[x][y]=1;
                            cnt++;
                            printf("%d %d\n",x,y);
                            if(cal(x,y)==2){
                                num0--;
                            }
                            if(num0==0 ||cnt==n){
                                break;
                            }
                        }
                        if(num0==0||cnt==n){
                            break;
                        }
                        turn=(turn+1)%4;
                    }
                    else if(turn==1){
                        for(int i=1;i<=step;i++){
                            y++;
                            vis[x][y]=1;
                            cnt++;
                            printf("%d %d\n",x,y);
                            if(cal(x,y)==2){
                                num0--;
                            }
                            if(num0==0 ||cnt==n){
                                break;
                            }
                        }
                        if(num0==0||cnt==n){
                            break;
                        }
                        turn=(turn+1)%4;
                    }
                    else if(turn==2){
                        for(int i=1;i<=step;i++){
                            x--;
                            vis[x][y]=1;
                            cnt++;
                            printf("%d %d\n",x,y);
                            if(cal(x,y)==2){
                                num0--;
                            }
                            if(num0==0 ||cnt==n){
                                break;
                            }
                        }
                        if(num0==0||cnt==n){
                            break;
                        }
                        turn=(turn+1)%4;
                    }
                    else if(turn==3){
                        for(int i=1;i<=step;i++){
                            y--;
                            vis[x][y]=1;
                            cnt++;
                            printf("%d %d\n",x,y);
                            if(cal(x,y)==2){
                                num0--;
                            }
                            if(num0==0 ||cnt==n){
                                break;
                            }
                        }
                        if(num0==0||cnt==n){
                            break;
                        }
                        turn=(turn+1)%4;
                    }
                }
                if(num0==0||cnt==n){
                    break;
                }
            }
            if(cnt!=n){
                if(turn==0){//右轉下
                    while(cnt<n){
                        y--;
                        printf("%d %d\n",x,y);
                        cnt++;
                    }
                }
                else if(turn==1){//上轉右
                    while(cnt<n){
                        x++;
                        printf("%d %d\n",x,y);
                        cnt++;
                    }
                }
                else if(turn==2){//左轉上
                    while(cnt<n){
                        y++;
                        printf("%d %d\n",x,y);
                        cnt++;
                    }
                }
                else if(turn==3){//下轉左
                    while(cnt<n){
                        x--;
                        printf("%d %d\n",x,y);
                        cnt++;
                    }
                }
            }
        }
    }
}

E Two Matchings

題意

給你一個數列,從a1−>an,你需要找到兩個匹配序列p,q其中\(pi≠qi\)
使得(∑(n i=1)abs(ai−api))/2+(∑(n i=1)abs(ai−aqi))/2最小,問這個最小值是多少。n保證為偶數,sum(n)<=2e5
匹配序列的定義為Pi!=i並且對於所有的Ppi=i

解法

第一個序列P是可以確定的最小值,也就是對a陣列排序後將每相鄰的兩個相互連線,序列Q使用DP維護最小值,至於如何DP,
我們可以觀察題目得出所有長度大於8的序列,都可以分解為長度為4或者長度為6的序列,剩下的就是貪心計算最小邊權之和。

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
int a[N];
ll dp[N];
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",a+i);
		sort(a+1,a+1+n);
		dp[0]=0;
		dp[2]=inf;
		dp[4]=a[4]-a[1];
		for(int i=6;i<=n;i+=2)
			dp[i]=min(dp[i-4]+a[i]-a[i-3],dp[i-6]+a[i]-a[i-5]);
		printf("%lld\n",2*dp[n]);
	}
    return 0;
}

F Fraction Construction Problem

題意

給兩個正整數a和b,要求找到四個正整數c,d,e,f,滿足\(\frac{a}{b}=\frac{c}{d}-\frac{e}{f}\)

解法

此題看到數學公式想到去構造,想怎麼構造出來這個式子

①如果分子分母存在公共因子,b不是質數,構造\(\frac{a}{b}=\frac{a+x}{b}-\frac{x}{b}\),然後我們對分子分母同除以a,b的一個公因子,我們設為g,那麼就會有\(\frac{a}{b}=\frac{\frac{a+x}{g}}{\frac{b}{g}}-\frac{\frac{x}{g}}{\frac{b}{g}}\),由於x是任意的,x=g,所以\(\frac{a}{b}=\frac{\frac{a}{g}+1}{\frac{b}{g}}-\frac{1}{\frac{b}{g}}\)

②如果分子分母最簡化了,分母的質因子不超過一個,比如8,16,那麼無解。

③分子分母最簡化,分母的質因子超過一個,有\(df=b\)\(gcd(d,f)=1\)。那麼只要找到d,f,然後令\(cf-ed=a\),也就是\(fx-dy=gcd(f,d)*a\)。熟悉的配料,擴充套件歐幾里得。

程式碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=2e6+10;

int min_prim[maxn],prim[maxn];
int vis[maxn];

void init()
{
    int m=sqrt(maxn);
    for (int i=2; i<m; i++){
        if (!vis[i]){
            min_prim[i]=i;
            for (int j=i*i; j<maxn; j+=i){
                vis[j]=1;
                if (!min_prim[j]) min_prim[j]=i;
            }
        }
    }
    for (int i=1; i<maxn; i++){
        if (!vis[i]) continue;
        prim[i]++;
        int x=i;
        while (x%min_prim[i]==0) x/=min_prim[i];
        if (x!=1) prim[i]++;
    }
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if (!b) {
        x=1; y=0;
        return a;
    }
    ll d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

int main()
{
    int t;
    init();
    scanf ("%d",&t);
    while (t--){
        int a,b;
        scanf ("%d%d",&a,&b);
        int g=__gcd(a,b);
        if (g!=1) {
            printf("%d %d %d %d\n",a/g+1,b/g,1,b/g);
            continue;
        }
        if (prim[b]<=1) {printf("-1 -1 -1 -1\n"); continue;}
        ll d=1,f=b;
        while (f%min_prim[b]==0) {
            f/=min_prim[b];
            d*=min_prim[b];
        }
        ll c,e;
        exgcd(f,d,c,e);
        e=-e;
        while (e<0 || c<0) e+=f,c+=d;
        printf ("%lld %lld %lld %lld\n",1LL*c*a,d,1LL*e*a,f);
    }
    return 0;
}

G Operating on a Graph

題意

解法

程式碼

//將內容替換成程式碼

L Problem L is the Only Lovely Problem

題意

簽到題。給一個字串,要求判斷是不是以“lovely”為開頭,如果是,輸出lovely,否則輸出ugly。

解法

使用string的find函式即可,注意大小寫。

程式碼

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e6+5;
const ll mod =1000000007;
int main(){
	string s;
	while(cin>>s){
		int len=s.length();
		for(int i=0;i<len;i++){
			if(isupper(s[i])){
				s[i]=tolower(s[i]);
			}
		}
		int p;
		if((p=s.find("lovely"))==0){
			cout<<"lovely"<<endl;
		}else{
			cout<<"ugly"<<endl;
		}
	} 
}