1. 程式人生 > 實用技巧 >2020ICPC上海站總結&補題

2020ICPC上海站總結&補題

這場我們隊只過了兩道題,五個小時一直自閉中。


B題思維題,沒想出來

C題數位DP,比賽的時候沒仔細看題,覺得太複雜就沒看

D題分類討論加二分,捋不清楚分哪幾種

G題簽到題

I題讀錯了,wa在了m==1的情況,m==1時,圓心點不計入

L題讀完題就在想一個最優解,結果做法是列舉有可能是最優的點,線性複雜度

M題簽到題,隊友說有思路,我就直接看別的題去了,結果調了挺久


B題:

題意:

給一個n、m,以及兩個n×m的圖,X代表周圍地雷的個數,.代表地雷。

將第二個圖修改(.變為X或X變為.)至多m*n/2次使得X的值的總和等於第一個圖的X的值的總和

輸出修改後的圖

思路:

X的值的綜合即為每個九宮格中X與.的對數之和

將X變換為.,.變換為X,X的值的總和不變。

故做法為變換不超過m*n/2次數下,修改成圖1的原圖或圖一.與X互換後的圖

程式碼:

#include<bits/stdc++.h>

using namespace std;
char a[1005][1005],b[1005][1005];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>a[i][j];
        }
    }
    
int diff=0; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cin>>b[i][j]; if(b[i][j]!=a[i][j]) diff++; } } if(diff>n*m/2){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ a[i][j]=(a[i][j]=='
X'?'.':'X'); } } } for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cout<<a[i][j]; } cout<<endl; } }


D題:

在一個長為n的線段,左端點為0,右端點為n

有兩個端點分別位於p1,p2,它們的速度分別為v1,v2

問最少需要多少時間,可以使得p1,p2的路程覆蓋整條線段

思路:

分類討論:

先令p1<p2

①p1走完全程

②p2走完全程

③p1向右走完全部,p2向左走完全部

④p1走完左邊的全部,p2走完右邊的全部,剩餘中間的部分p1與p2共同走完,二分路程或時間即可

程式碼:

#include<bits/stdc++.h>

using namespace std;
const double eps=1e-8;
double n,p1,p2,v1,v2;
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>p1>>v1>>p2>>v2;
        if(p1>p2){swap(p1,p2);swap(v1,v2);};
        //p1走完全程
        double ans=min(p1+n,2*n-p1)/v1;
        //p2走完全程
        ans=min(ans,min(2*n-p2,p2+n)/v2);
        //p1走完右邊全程,p2走完左邊全程
        ans=min(ans,max((n-p1)/v1,p2/v2));
        //p1走完左邊全程,p2走完右邊全程,二分分界點
        double l=p1,r=p2;
        while(r-l>eps){
            double mid=(l+r)/2;
            double ans1=min(p1+mid,2*mid-p1)/v1,ans2=min(2*n-p2-mid,p2+n-2*mid)/v2;
            ans=min(ans,max(ans1,ans2));
            if(ans1<ans2)//p1先走完 分界點向右移
                l=mid;
            else
                r=mid;
        }
        printf("%.10lf\n",ans);
    }
    
}


G題:

簽到題

題意:

對於斐波那契數列 1、1、2、3、5、8、13、21、34……

給一個n計算所有g(fi,fj)的和(i<j)

對於a和b,當a*b為偶數時g(a,b)=1,否則g(a,b)=0;

思路:

觀察斐波那契數列易得,當i%3==0時fi為偶數

當兩個數都不是奇數時,兩個數乘積為偶數

從數列中選出兩對共有C2(n)種選法

偶數的個數有n/3個 則奇數的個數有n-n/3

故答案為C2(n)-C2(n-n/3)

程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll C2(ll a){
    return a*(a-1)/2;
}
int main(){
    ll n;
    cin>>n;
    ll m=n/3;
    cout<<C2(n)-C2(n-m)<<endl;
}


I題:

給一個n和m。

有n個圓心在(0,0)的圓,第i個圓的半徑為i

有m條經過(0,0)的線將這些圓分為等份

線與線、線與圓會產生交點,求這些交點兩兩之間的最短路徑和

思路:

對於外層圓上的點到內層圓上的點的最短路徑必然要先向圓心走到同層的位置

故可以通過遞推的方式來做

唯一要注意的是m=1時不包括圓心,m>1時包括了圓心

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
const int maxn = 1e6 + 10;
const double PI=acos(-1);
double a[505],b[505];
int n,m;
int main()
{
    fastio;
    cin>>n>>m;
    double cnt=0;
    for(int i=1;i<m;i++){
        if(PI*i<2*m)
            cnt+=PI*i/m;
        else
            cnt+=2;
        //cout<<cnt<<endl;
    }
    cnt*=2;
    cnt+=2;
    a[1]=cnt;b[1]=a[1];
    //cout<<a[1]<<endl;
    for(int i=2;i<=n;i++){
        a[i]=a[i-1]+(i-1)*(2*m)+i*b[1];
        b[i]=i*b[1];
    }
    double ans=0;
    for(int i=1;i<=n;i++){
        ans+=2*m*(a[i]-b[i])+m*b[i];
        if(m>1)
            ans+=2*i*m;
    }
    printf("%.10lf\n",ans);
    
    return 0;

}


L題:

題意:

有一個網格圖n×m

左下點為(0,0),右上點為(n,m)

從一個點可以到達任意點,但路徑不能出現其他點(或者說,不能出現兩條路徑是相連線的)

求從(0,0)到(n,m)的最短路徑

思路:

如果gcd(n,m)==1,則從(0,0)到(n,m)上無其他點,最短路徑為√(n2+m2)

否則,路徑分為兩段 從(0,0)到(x,y),從(x,y)到(n,m)

列舉每個x,對於每個x,y的值在x*m/n附近取值一定是最優的

程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll gcd(ll a,ll b){
    return a%b?gcd(b,a%b):b;
}
int main(){
    ll t,n,m;
    cin>>t;
    while(t--){
        cin>>n>>m;
        double ans;
        if(gcd(n,m)==1){
            ans=sqrt(n*n+m*m);
        }else{
            ans=1e15;
            for(ll x=1;x<n;x++){
                ll y=x*m/n,y1,y2;
                if(x*m==y*n){y1=y-1,y2=y+1;}
                else{y1=y,y2=y+1;}
                if(gcd(y1,x)==1&&gcd(m-y1,n-x)==1){
                    ans=min(ans,sqrt(x*x+y1*y1)+sqrt((n-x)*(n-x)+(m-y1)*(m-y1)));
                }
                if(gcd(y2,x)==1&&gcd(m-y2,n-x)==1){
                    ans=min(ans,sqrt(x*x+y2*y2)+sqrt((n-x)*(n-x)+(m-y2)*(m-y2)));
                }
            }
        }
        printf("%.15lf\n",ans);
    }
}


M題: