第四次訓練

A 生活大爆炸版 石頭剪刀布

題目描述

石頭剪刀布是常見的猜拳遊戲:石頭勝剪刀,剪刀勝布,布勝石頭。如果兩個人出拳一 樣,則不分勝負。在《生活大爆炸》第二季第 8 集中出現了一種石頭剪刀布的升級版遊戲。 升級版遊戲在傳統的石頭剪刀布遊戲的基礎上,增加了兩個新手勢: 斯波克:《星際迷航》主角之一。 蜥蜴人:《星際迷航》中的反面角色。 這五種手勢的勝負關係如表一所示,表中列出的是甲對乙的遊戲結果。
表一 石頭剪刀布升級版勝負關係

現在,小 A 和小 B 嘗試玩這種升級版的猜拳遊戲。已知他們的出拳都是有週期性規律 的,但週期長度不一定相等。例如:如果小 A 以“石頭-布-石頭-剪刀-蜥蜴人-斯波克”長度 爲 6 的週期出拳,那麼他的出拳序列就是“石頭-布-石頭-剪刀-蜥蜴人-斯波克-石頭-布-石頭 -剪刀-蜥蜴人-斯波克-……”,而如果小 B 以“剪刀-石頭-布-斯波克-蜥蜴人”長度爲 5 的周 期出拳,那麼他出拳的序列就是“剪刀-石頭-布-斯波克-蜥蜴人-剪刀-石頭-布-斯波克-蜥蜴人 -……” 已知小 A 和小 B 一共進行 N 次猜拳。每一次贏的人得 1 分,輸的得 0 分;平局兩人都 得 0 分。現請你統計 N 次猜拳結束之後兩人的得分。

輸入

第一行包含三個整數:N,NA,NB,分別表示共進行 N 次猜拳、小 A 出拳的週期長度, 小 B 出拳的週期長度。數與數之間以一個空格分隔。
第二行包含 NA 個整數,表示小 A 出拳的規律
第三行包含 NB 個整數,表示小 B 出拳 的規律。
其中,0 表示“剪刀”,1 表示“石頭”,2 表示“布”,3 表示“蜥蜴人”, 4 表示 “斯波克”。數與數之間以一個空格分隔。

輸出

輸出一行, 包含兩個整數,以一個空格分隔,分別表示小 A、小 B 的得分。

樣例輸入

10 5 6
0 1 2 3 4
0 3 4 2 1 0

樣例輸出

6 2

提示

【數據說明】
對於 100%的數據,0 < N ≤ 200,0 < NA ≤ 200, 0 < NB ≤ 200。

這個題模擬或者打表就好了,比賽時我是直接模擬做的,想到啥寫啥,,,,打表的話把各種結果都實現存起來就行。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
int n,x,y;
int a[210],b[210];
int main()
{
    while(~scanf("%d%d%d",&n,&x,&y)){
            met(a,0);
            met(b,0);
            int ans1=0;
            int ans2=0;
        for(int i=1;i<=x;i++)
            sc(a[i]);
            for(int i=1;i<=y;i++)
                sc(b[i]);
            for(int i=x+1;i<=n;i++)
                a[i]=a[i-x];
            for(int i=y+1;i<=n;i++)
                b[i]=b[i-y];
            for(int i=1;i<=n;i++){
                if(a[i]==0){
                    if(b[i]==2||b[i]==3)
                        ans1++;
                    if(b[i]==1||b[i]==4)
                        ans2++;
                }
                else if(a[i]==1){
                    if(b[i]==0||b[i]==3)
                        ans1++;
                    if(b[i]==2||b[i]==4)
                        ans2++;
                }
                else if(a[i]==2){
                    if(b[i]==0||b[i]==3)
                        ans2++;
                    if(b[i]==1||b[i]==4)
                        ans1++;
                }
                else if(a[i]==3){
                    if(b[i]==2||b[i]==4)
                        ans1++;
                    if(b[i]==0||b[i]==1)
                        ans2++;
                }
                else if(a[i]==4){
                    if(b[i]==0||b[i]==1)
                        ans1++;
                    if(b[i]==2||b[i]==3)
                       ans2++;
                }
            }
            printf("%d %d\n",ans1,ans2);
    }
}

B 聯合權值

題目描述

無向連通圖 G 有 n 個點,n-1 條邊。點從 1 到 n 依次編號,編號爲 i 的點的權值爲 Wi , 每條邊的長度均爲 1。圖上兩點(u, v)的距離定義爲 u 點到 v 點的最短距離。對於圖 G 上的點 對(u, v),若它們的距離爲 2,則它們之間會產生Wu*Wv的聯合權值。 請問圖 G 上所有可產生聯合權值的有序點對中,聯合權值最大的是多少?所有聯合權 值之和是多少?

輸入

第一行包含 1 個整數 n。 接下來 n-1 行,每行包含 2 個用空格隔開的正整數 u、v,表示編號爲 u 和編號爲 v 的點 之間有邊相連。 最後 1 行,包含 n 個正整數,每兩個正整數之間用一個空格隔開,其中第 i 個整數表示 圖 G 上編號爲 i 的點的權值爲 Wi。

輸出

輸出共 1 行,包含 2 個整數,之間用一個空格隔開,依次爲圖 G 上聯合權值的最大值 和所有聯合權值之和。由於所有聯合權值之和可能很大,輸出它時要對 10007 取餘。

樣例輸入

5
1 2
2 3
3 4
4 5
1 5 2 3 10

樣例輸出

20 74

提示

【樣例說明】

本例輸入的圖如上所示,距離爲 2 的有序點對有(1,3)、(2,4)、(3,1)、(3,5)、(4,2)、(5,3)。
其聯合權值分別爲 2、15、2、20、15、20。其中最大的是 20,總和爲 74。
【數據說明】
對於 30%的數據,1 <n ≤ 100;
對於 60%的數據,1 < n ≤ 2000;
對於 100%的數據,1 < n ≤ 200,000,0 < wi ≤ 10,000。

 

這個題個人感覺還是很難的,,比賽時看了一眼先跳過去了,結果就一直沒做。剛剛又補了一下午才過的。。。

這個題我還是先天真的交了一發暴力N^2做的,,果然T了,,然後一直在優化。。

通過題目我們知道這個圖是個樹,既然是樹,那麼就不存在環,那麼通過一個點連着的兩個點的距離肯定就是2了,這兩個點肯定不會相連,於是我們遍歷這個圖上的每個點,只要將這個點相連的點互相乘一下,再加起來就可以求出和來了, 而最大值可以在每次遍歷的時候比較得出。。

而這個和具體的話要用到前綴和,乘法分配律的思想。

如果一個點E,有A,B,C,D四個點和它相連。那麼這四個點互乘求和就是這個點E的答案,

這裏就用A,B,C,D代表每個點的權值。

那麼和ans=AB+AC+AD+BA+BC+BD+CA+CB+CD+DA+DB+DC

把重複的合併ans=2*(AB+AC+AD+BC+BD+CD)

                              =2*(A*(B+C+D)+B*(C+D)+C*D)

這樣化簡完是不是看出來瞭如何使用前綴和了,這裏用一個變量ans1把每次遍歷得到的值加起來即可。

具體看代碼吧

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int mod=10007;
const int N = 2e5+10;
ll n,ans,maxn,a[N],head[N*2],tot;
struct edge
{
    int v;
    int next;
    edge()
    {
        v=0;
        next=0;
    }
}e[N*4];                  //鏈式前向星存圖
void add(int x,int y)
{
    e[++tot].next=head[x];
    e[tot].v=y;
    head[x]=tot;
}
void solve(int x)
{
    ll ans1=0;              //前綴和
    ans1=a[e[head[x]].v]%mod;    //先賦值給第一個點的權值
    int s=e[head[x]].next;        //第二條邊
    ll mm=a[e[head[x]].v];     //求最大值,題目沒有說取模
       for(int i=s;i;i=e[i].next){
          ans=(ans+a[e[i].v]*ans1)%mod;      //乘前綴和在累加
          ans1=(ans1+a[e[i].v])%mod;         //更新前綴和
          maxn=max(maxn,a[e[i].v]*mm);      //更新最大值
          mm=max(mm,a[e[i].v]);             //如果有比當前值大的就更新
       }
}
void init()
{
    tot=0;
    ans=0;
    maxn=0;
    met(head,0);
    met(a,0);
}
int main()
{
    while(~scanf("%lld",&n)){
            init();
            int x,y;
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
            for(int i=1;i<=n;i++)
                solve(i);
        printf("%lld %lld\n",maxn,(ans*2)%mod);
    }
}

     D 質因數分解

題目描述

已知正整數 n 是兩個不同的質數的乘積,試求出較大的那個質數。

輸入

輸入只有一行,包含一個正整數 n。

輸出

輸出只有一行,包含一個正整數 p,即較大的那個質數。

樣例輸入

21

樣例輸出

7

提示

【數據範圍】
對於 60%的數據,6 ≤ n ≤ 1000。
對於 100%的數據,6 ≤ n ≤ 2*10e9。

這個用歐拉篩法應該可以做,注意一下開的數組的大小,篩出來的素數從小到大找,能被n整除,就n除以這個數得出答案。

但是還有另一種方法,根據質數的唯一分解定理,如果給出的n能被表示成兩個質數的乘積,那麼它就不能被合數整除,我們可以直接從2開始遍歷到n,如果能被整除,同樣除以這個數得出答案。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
int n;
int main()
{
    while(~scanf("%d",&n)){
        for(int i=2;i<=n;i++){
            if(n%i==0){
                printf("%d\n",n/i);
                break;
            }
        }
    }
    return 0;
}

E 尋寶

題目描述

傳說很遙遠的藏寶樓頂層藏着誘人的寶藏。
小明歷盡千辛萬苦終於找到傳說中的這個藏 寶樓,藏寶樓的門口豎着一個木板,上面寫有幾個大字:尋寶說明書。
說明書的內容如下: 藏寶樓共有 N+1 層,最上面一層是頂層,頂層有一個房間裏面藏着寶藏。
除了頂層外, 藏寶樓另有 N 層,每層 M 個房間,這 M 個房間圍成一圈並按逆時針方向依次編號爲 0,…, M-1。
其中一些房間有通往上一層的樓梯,每層樓的樓梯設計可能不同。
每個房間裏有一個 指示牌,指示牌上有一個數字 x,表示從這個房間開始按逆時針方向選擇第 x 個有樓梯的房 間(假定該房間的編號爲 k),從該房間上樓,上樓後到達上一層的 k 號房間。
比如當前房 間的指示牌上寫着 2,則按逆時針方向開始嘗試,找到第 2 個有樓梯的房間,從該房間上樓。
如果當前房間本身就有樓梯通向上層,該房間作爲第一個有樓梯的房間。
尋寶說明書的最後用紅色大號字體寫着:“尋寶須知:幫助你找到每層上樓房間的指示 牌上的數字(即每層第一個進入的房間內指示牌上的數字)總和爲打開寶箱的密鑰”。 請幫助小明算出這個打開寶箱的密鑰。

 

輸入

第一行 2 個整數 N 和 M,之間用一個空格隔開。N 表示除了頂層外藏寶樓共 N 層樓, M 表示除頂層外每層樓有 M 個房間。
接下來 N*M 行,每行兩個整數,之間用一個空格隔開,每行描述一個房間內的情況, 其中第(i-1)*M+j 行表示第 i 層 j-1 號房間的情況(i=1, 2, …, N;j=1, 2, … ,M)。第一個整數 表示該房間是否有樓梯通往上一層(0 表示沒有,1 表示有),第二個整數表示指示牌上的數 字。注意,從 j 號房間的樓梯爬到上一層到達的房間一定也是 j 號房間。
最後一行,一個整數,表示小明從藏寶樓底層的幾號房間進入開始尋寶(注:房間編號 從 0 開始)。

輸出

輸出只有一行,一個整數,表示打開寶箱的密鑰,這個數可能會很大,請輸出對 20123 取模的結果即可。

樣例輸入

2 3
1 2
0 3
1 4
0 1
1 5
1 2
1

樣例輸出

5

提示

【輸入輸出樣例說明】
第一層: 0 號房間,有樓梯通往上層,指示牌上的數字是 2; 1 號房間,無樓梯通往上層,指示牌上的數字是 3; 2 號房間,有樓梯通往上層,指示牌上的數字是 4;
第二層: 0 號房間,無樓梯通往上層,指示牌上的數字是 1; 1 號房間,有樓梯通往上層,指示牌上的數字是 5; 2 號房間,有樓梯通往上層,指示牌上的數字是 2;
小明首先進入第一層(底層)的 1 號房間,記下指示牌上的數字爲 3,然後從這個房間 開始,沿逆時針方向選擇第3 個有樓梯的房間 2 號房間進入,上樓後到達第二層的2 號房間, 記下指示牌上的數字爲 2,由於當前房間本身有樓梯通向上層,該房間作爲第一個有樓梯的 房間。因此,此時沿逆時針方向選擇第 2 個有樓梯的房間即爲 1 號房間,進入後上樓梯到達 頂層。這時把上述記下的指示牌上的數字加起來,即 3+2=5,所以打開寶箱的密鑰就是 5。
【數據範圍】
N<=10000
M<=100
X<=1e6

 

這也是個模擬,不過這個模擬感覺有點噁心,比賽時就是因爲這個題,,耽誤了時間,這個也沒過,,orz,就交晚了50多秒。。

模擬就好了,再輸入的時候就把有樓梯的房間的個數記下來,然後每進入一層就先加上值,在繞就好了。。具體看代碼吧,這個看一下應該就清楚了。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int mod=20123;
const int N=1e4+10;
int n,a[N][110],m,ans;
bool vis[N][110];
int main()
{
    while(~scanf("%d%d",&n,&m)){
            ans=0;
            met(b,0);
            met(vis,0);
            met(a,0);
        for(int i=1;i<=n;i++){
                int t=0;
        for(int j=0;j<m;j++){
            sc(vis[i][j]);
            sc(a[i][j]);
            if(vis[i][j])      //有沒有樓梯
                a[i][m]++; //因爲房間個數是從0~m-1,所以用第m個存這一層有樓梯的房間個數
        }
        }
        int s;
        sc(s);
        for(int i=1;i<=n;i++){
                ans=(ans+a[i][s]%mod)%mod;
            int p=0;
            for(int j=s;;j++){
               if(j==m)     //到頭了,再轉回去
               j=0;
              if(vis[i][j]==1)
                p++;
                int k=a[i][s]%a[i][m];
                if(k==0)            //如果取模爲0,就代表要轉的房間爲這一層有樓梯的房間個數
                    k=a[i][m];
              if(p==k){
               s=j;
               break;
              }
            }
        }
        printf("%d\n",ans);
    }
}

 

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