2015藍橋杯B組初賽


方程整數解

題意:

給定n,求出 a^2 + b^2 + c^2 = n(1<=n<=10000)的所有解,解要保證c>=b>=a>=1。

思路:

枚舉a和b,利用a和b推c,判斷是否合法即可

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n;
    while(cin>>n){
        int ok=0;
        for(int a=1;a*a<n;a++){
            for(int b=a;a*a+b*b<n;b++){
                int cc=n-(a*a+b*b);
                int c=sqrt(cc);
                if(c>=b&&c*c==cc){
                    cout<<a<<' '<<b<<' '<<c<<endl;
                    ok=1;
                }
            }
        }
        if(!ok)cout<<"No Solution"<<endl;
    }
    return 0;
}


星系炸彈

題意:

有一個貝塔炸彈,a年b月c日放置,定時爲n天,請你計算它爆炸的準確日期。
n<=1e3

思路:

挺煩日期題的。
因爲n比較小,只有1000,所以可以直接一天一天加,很穩。

code:

#include<bits/stdc++.h>
using namespace std;
int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};//每個月的天數
bool check(int n){//判斷閏年
    if(n%4==0&&n%100!=0)return 1;
    if(n%400==0)return 1;
    return 0;
}
signed main(){
    int a,b,c,n;
    while(cin>>a>>b>>c>>n){
        while(n--){
            if(check(a))s[2]=29;//如果是閏年則2月有29天
            else s[2]=28;
            c++;
            if(c>s[b])c=1,b++;
            if(b>12)b=1,a++;
        }
        printf("%04d-%02d-%02d\n",a,b,c);
    }
    return 0;
}

奇妙的數字

題意:

一個數的平方和立方正好把0~9的10個數字每個用且只用了一次。你能猜出這個數字是多少嗎?

思路:

因爲這個數一定存在,寫個程序直接while(1)從小到大不斷枚舉數x,然後把x2和x3拆位判斷是否每種數字都出現一次就行了。
最後計算出答案爲69。


牌型種數

題意:

從52張牌取13張,如果不考慮花色,只考慮點數,也不考慮自己得到的牌的先後順序
自己手裏能拿到的初始牌型組合一共有多少種呢?

思路:

因爲只需要輸出答案,直接枚舉每種點數的牌的數量,可以dfs也可以直接13個for循環。
答案爲3598180。


手鍊樣式

題意:

小明有3顆紅珊瑚,4顆白珊瑚,5顆黃瑪瑙。
他想用它們串成一圈作爲手鍊,送給女朋友。
現在小明想知道:如果考慮手鍊可以隨意轉動或翻轉,一共有多少不同的組合樣式?

思路:

因爲只需要輸出答案,所以可以不想那麼多,直接開map標記+全排列計算答案。
注意題目說明可以隨意轉動和翻轉,別漏了。
算出答案爲1170。

去別人那找個了組合數學的結論,順便掛下:
在這裏插入圖片描述

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
map<string,int>mark;
signed main(){
    string s="aaabbbbccccc";//3+4+5=12
    int ans=0;
    do{
        string t=s;
        if(!mark[t])ans++;
        for(int i=0;i<12;i++){//把翻轉和循環都標記掉
            mark[t]=1;
            reverse(t.begin(),t.end());//翻轉
            mark[t]=1;
            reverse(t.begin(),t.end());//翻回來
            //循環一下,把第一個字符放到最後一個
            char d=*t.begin();//取開頭
            t.erase(t.begin());//刪掉開頭
            t+=d;//放到結尾
        }
    }while(next_permutation(s.begin(),s.end()));
    cout<<ans<<endl;
    return 0;
}

飲料換購

題意:

開始有n瓶飲料,3個瓶蓋可以換一瓶,問最後一共喝到多少瓶。

code:

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

signed main(){
    int n;
    while(cin>>n){
        int ans=n;
        //n爲瓶蓋數
        while(n>=3){
            int t=n/3;//換到的飲料數
            n-=t*3;//瓶蓋減少
            ans+=t;//累加答案
            n+=t;//瓶蓋增加
        }
        cout<<ans<<endl;
    }
    return 0;
}

壘骰子(矩陣快速冪)

題意:

賭聖atm晚年迷戀上了壘骰子,就是把骰子一個壘在另一個上邊,不能歪歪扭扭,要壘成方柱體。
經過長期觀察,atm 發現了穩定骰子的奧祕:有些數字的面貼着會互相排斥!
我們先來規範一下骰子:1 的對面是 4,2 的對面是 5,3 的對面是 6。
假設有 m 組互斥現象,每組中的那兩個數字的面緊貼在一起,骰子就不能穩定的壘起來。
atm想計算一下有多少種不同的可能的壘骰子方式。
兩種壘骰子方式相同,當且僅當這兩種方式中對應高度的骰子的對應數字的朝向都相同。
由於方案數可能過多,請輸出模 1e9+ 7 的結果。

思路:

非常容易就能想到dp的解法,但是硬莽的話只能過百分60的數據(n<=100)。
對於百分100的數據,n<=1e9,數據這麼大肯定是往快速冪方向想,應該是矩陣快速冪。
d(i,j)表示i個骰子,第j面朝上的做法,顯然d(i,j)=sigma d(i-1,k),其中j的對立面和k不互斥。
發現這個遞推關係只有最多6個加法,可以直接一次6階矩陣轉移計算出d(i),配合矩陣快速冪就很快了。
利用互斥關係構造轉移矩陣,矩陣快速冪即可。
注意每個固定住上下面的骰子可以可以旋轉,因此答案要乘上4n(4個面)。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=6;
const int mod=1e9+7;
struct Node{
    int a[N][N];
};
int g[6][6];
int dif[6]={3,4,5,0,1,2};//每個數對面的數
Node mul(Node A,Node B){
    Node C;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)C.a[i][j]=0;
    for(int k=0;k<N;k++)for(int i=0;i<N;i++)if(A.a[i][k]){
        for(int j=0;j<N;j++)if(B.a[k][j]){
            C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
        }
    }
    return C;
}
Node Pow(Node a,int b){
    Node ans;
    for(int i=0;i<N;i++)for(int j=0;j<N;j++)ans.a[i][j]=(i==j);
    while(b){
        if(b&1)ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}
int ppow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
signed main(){
    int n,m;
    while(cin>>n>>m){
        for(int i=0;i<6;i++)for(int j=0;j<6;j++)g[i][j]=1;
        for(int i=1;i<=m;i++){//輸入m對互斥關係
            int a,b;
            cin>>a>>b;
            a--,b--;
            g[a][b]=g[b][a]=0;
        }
        Node x;
        for(int i=0;i<6;i++)for(int j=0;j<6;j++)x.a[i][j]=0;
        for(int i=0;i<6;i++)for(int j=0;j<6;j++){
            if(g[i][j]){//dif[j]可從i轉移
                x.a[i][dif[j]]=1;
            }
        }
        Node p=Pow(x,n-1);
        int ans[6]={0};//ans[i]即d[n][i]
        for(int i=0;i<6;i++){//計算ans[],因爲初始矩陣爲1*6的全1矩陣,所以ans[i]就是x的第i列相加
            for(int j=0;j<6;j++){
                ans[i]+=p.a[i][j];
            }
        }
        int sum=0;
        for(int i=0;i<6;i++)sum=(sum+ans[i])%mod;
        sum=sum*ppow(4,n)%mod;
        cout<<sum<<endl;
    }
    return 0;
}

災後重建(壓軸題)

題意:

給定n個點m條帶權邊的無向圖
q組詢問,每組詢問問(L,R,K,C),要求輸出編號在[L,R]之間,且編號模K等於C的點,現在要你選出一些邊,使得這些點連通,問選出的邊權的最大值最小爲多少(保證每次詢問至少存在兩個點)。
(n<=5e4,m<=2e5,q<=5e5)
(L,R,K均在[1,n]以內,C在K以內)
時限5s

思路:

網上搜到的分級數據:
百分30數據nmq<=30
百分60數據nmq<=2000
百分100數據n<=5e4,m<=2e5,q<=1e6

最小生成樹能使得所有點聯通,因此先構建出最小生成樹,記錄樹邊,其他多餘的邊沒有用。

百分30:
做法1.能使得所有點聯通的就是最小生成樹,對於每組詢問 ,標記模k等於c的點,然後用已經記錄的樹邊跑kruskal,如果在連接某個邊之後,標記的點能夠聯通,那麼這個點的邊權就是答案。
做法2.二分最大邊權的最小值,然後在最小生成樹上從任意標記的點開始dfs,只走邊權小於等於當前二分的值的路徑,最後判斷能否把標記的點全部搜到。

百分60:
1.最小生成樹+LCA,在最小生成樹上記錄任意兩點之間路徑的最大值
2.kruskal重構樹+LCA,也是記錄兩點之間最大路徑最小值
對於每組詢問,對任意兩點之間的路徑最大值取max就是答案

百分100:
建議直接放棄


獎券數目

題意:

計算10000-99999中有多少個不帶4的數字,直接輸出答案

思路:

遍歷+拆位暴力判斷即可


三羊獻瑞

題意:

在這裏插入圖片描述

思路:

全排列枚舉每種漢字所代表的數,判斷是否合法即可

code:

#include<bits/stdc++.h>
using namespace std;
int a[10]={0,1,2,3,4,5,6,7,8,9};
//祥瑞生輝三羊獻氣
signed main(){
    do{
        if(a[0]&&a[4]){
            int aa=a[0]*1000+a[1]*100+a[2]*10+a[3];
            int bb=a[4]*1000+a[5]*100+a[6]*10+a[1];
            int sum=a[4]*10000+a[5]*1000+a[2]*100+a[1]*10+a[7];
            if(aa+bb==sum){
                cout<<bb<<endl;
                break;
            }
        }
    }while(next_permutation(a,a+10));
    return 0;
}

加法變乘法

題意:

我們都知道:1+2+3+ … + 49 = 1225
現在要求你把其中兩個不相鄰的加號變成乘號,使得結果爲2015
比如:
1+2+3+…+1011+12+…+2728+29+…+49 = 2015 就是符合要求的答案。
請你尋找另外一個可能的答案,並把位置靠前的那個乘號左邊的數字提交。

思路:

枚舉兩個乘號的位置,判斷是否合法即可。
計算出答案爲16,另寫一個程序直接輸出。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n=49;
    for(int i=1;i<n;i++){//第一個乘號在i之後
        for(int j=i+2;j<n;j++){//第二個乘號在j之後
            int sum=0;
            for(int k=1;k<=i-1;k++)sum+=k;
            sum+=i*(i+1);
            for(int k=i+2;k<=j-1;k++)sum+=k;
            sum+=j*(j+1);
            for(int k=j+2;k<=n;k++)sum+=k;
            if(sum==2015)cout<<i<<endl;
        }
    }
    return 0;
}

移動距離

題意:

在這裏插入圖片描述

思路:

這題的題面有點誤導人,看題可能會以爲只能沿着那個數字順序走,其實是可以直接上下左右走的。
因此這題其實就是計算兩個點的笛卡爾距離,算出座標差即可。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int w,m,n;
int getr(int x){//計算在第幾行
    return x/w+(x%w!=0);
}
int getc(int x){//計算在第幾列
    int t=getr(x);
    x%=w;
    if(t%2){//奇數正向
        return x;
    }else{//反向
        return w-x+1;
    }
}
signed main(){
    while(cin>>w>>m>>n){//w爲一行的寬度
        int ans=abs(getr(m)-getr(n));
        ans+=abs(getc(m)-getc(n));
        cout<<ans<<endl;
    }
    return 0;
}

生命之樹(樹形dp)

題意:

給定一顆n個節點的樹,每個點有點權
現在要求選出一個點集,滿足:
點集之內的任意兩點之間的路徑上的點也在這個點集中
問選出的點集的最大點權和是多少

思路:

首先要看懂題,題意就是選擇一些點,去掉其他點之後,選擇的這些點在同一個連通塊中,求最大點權和
令d(i)爲選擇點i所能得到的最大值,樹形dp:
考慮子節點v,如果d(v)大於0,就選擇v
考慮父節點,如果選擇父節點能使得答案變大,那麼d(x)就是答案,回溯到x的時候就能更新出更大的d(x)>d(v)
所以不需要管父節點,並不失完備性。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
vector<int>g[maxm];
int a[maxm];
int d[maxm];
void dfs(int x,int fa){
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
        if(d[v]>0)d[x]+=d[v];
    }
    d[x]+=a[x];
}
signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,-1);
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,d[i]);
    }
    cout<<ans<<endl;
    return 0;
}

打印大X

題意:

小明希望用星號拼湊,打印出一個大X,他要求能夠控制筆畫的寬度和整個字的高度。
爲了便於比對空格,所有的空白位置都以句點符來代替。
要求輸入兩個整數m n,表示筆的寬度,X的高度。

思路:

觀察樣例發現X是對稱的,所以計算上面一半,下面一半倒着輸出就行了。
這題麻煩的點是他只給行數n,不給列數,要你自己算。
觀察樣例圖形可以發現,第n/2+1行星號的長度爲x,每向上一行就向左右拓展兩格
所以列數爲n/2*2+m。
後面的圖形打印就沒什麼好說的了。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e3+5;
char a[maxm][maxm];
signed main(){
    int x,n;
    while(cin>>x>>n){//x是X的寬度
        int m=n/2*2+x;//列
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=0;
        int space=0;//前後的空格長度
        for(int i=1;i<=n/2+1;i++){
            for(int j=space+1;j<=space+1+x-1;j++){
                a[i][j]='*';
            }
            for(int j=m-space;j>=m-space-x+1;j--){
                a[i][j]='*';
            }
            space++;
        }
        for(int i=1;i<=n/2+1;i++){
            for(int j=1;j<=m;j++){
                if(!a[i][j])a[i][j]='.';
                cout<<a[i][j];
            }
            cout<<endl;
        }
        for(int i=n/2;i>=1;i--){
            for(int j=1;j<=m;j++){
                cout<<a[i][j];
            }
            cout<<endl;
        }
    }
    return 0;
}

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