NOIP2015 普及組(Junior) 解題報告


1. 金幣

                                                                     (coin.cpp/c/pas)

 國王將金幣作為工資,發放給忠誠的騎士。第一天,騎士收到一枚金幣;之后兩天(第二天和第三天),每天收到兩枚金幣;之后三天(第四、五、六天),每天收到三枚金幣;之后四天(第七、八、九、十天),每天收到四枚金幣……;這種工資發放模式會一直這樣延續下去:當連續 N 天每天收到 N 枚金幣后,騎士會在之后的連續 N+1 天里,每天收到 N+1 枚金幣。

請計算在前 K 天里,騎士一共獲得了多少金幣。

 

【輸入格式】

輸入文件名為 coin.in。

 輸入文件只有 1 行,包含一個正整數 K,表示發放金幣的天數。

 

【輸出格式】

輸出文件名為 coin.out。

 輸出文件只有 1 行,包含一個正整數,即騎士收到的金幣數。

 

【輸入輸出樣例 1】

coin.in

coin.out

6

 

14

 

見選手目錄下的 coin/coin1.in 和 coin/coin1.ans。

【輸入輸出樣例 1 說明】

騎士第一天收到一枚金幣;第二天和第三天,每天收到兩枚金幣;第四、五、六天,每天收到三枚金幣。因此一共收到 1+2+2+3+3+3=14 枚金幣。

 

【輸入輸出樣例 2】

coin.in

coin.out

1000

 

29820

 

見選手目錄下的 coin/coin2.in 和 coin/coin2.ans。

 

【數據說明】

 對於 100%的數據,1 ≤ K ≤ 10,000。

 

【思路】

   枚舉。

【代碼】

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 5 using namespace std;
 6 
 7 int n;
 8 
 9 int S(int k) { return k*(k+1)/2; }
10 
11 int main() {
12     freopen("coin.in","r",stdin);
13     freopen("coin.out","w",stdout);
14      
15     cin>>n;
16     int k=1;
17     while(S(k)<n) k++;
18     if(S(k)>n) k--;
19     n-=S(k);
20     int ans=0,s=1;
21     FOR(i,1,k) {
22         ans+=s*i;
23         s++;
24     }
25     ans += n*s;
26     cout<<ans;
27     return 0;
28 }
View Code

 

                                                                    2. 掃雷游戲

                                                                                  (mine.cpp/c/pas)

 

 掃雷游戲是一款十分經典的單機小游戲。在 n 行 m 列的雷區中有一些格子含有地雷

(稱之為地雷格),其他格子不含地雷(稱之為非地雷格)。玩家翻開一個非地雷格時,該格將會出現一個數字——提示周圍格子中有多少個是地雷格。游戲的目標是在不翻出任何地雷格的條件下,找出所有的非地雷格。

現在給出n行m列的雷區中的地雷分布,要求計算出每個非地雷格周圍的地雷格數。

注:一個格子的周圍格子包括其上、下、左、右、左上、右上、左下、右下八個方向上與之直接相鄰的格子。

 

【輸入格式】

 輸入文件名為 mine.in。

 輸入文件第一行是用一個空格隔開的兩個整數n和m,分別表示雷區的行數和列數。

接下來 n 行,每行 m 個字符,描述了雷區中的地雷分布情況。字符’*’表示相應格子是地雷格,字符’?’表示相應格子是非地雷格。相鄰字符之間無分隔符。

 

【輸出格式】

 輸出文件名為 mine.out。

 輸出文件包含 n 行,每行 m 個字符,描述整個雷區。用’*’表示地雷格,用周圍的地雷個數表示非地雷格。相鄰字符之間無分隔符。

 

【輸入輸出樣例 1】

mine.in

mine.out

3 3 *??

???

?*?

 

*10

221

1*1

 

見選手目錄下的 mine/mine1.in 和 mine/mine1.ans。

【輸入輸出樣例 2】

mine.in

mine.out

2 3 ?*?

*??

 

2*1

*21

 

見選手目錄下的 mine/mine2.in 和 mine/mine2.ans。

【輸入輸出樣例 3】

見選手目錄下的 mine/mine3.in 和 mine/mine3.ans。

 

【數據說明】  對於 100%的數據,1≤n≤100,1≤m≤100。

 

【思路】

   枚舉+統計。

【代碼】

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 5 using namespace std;
 6 
 7 const int maxn = 100+10;
 8 
 9 char G[maxn][maxn];
10 int n,m;
11 
12 int A(int i,int j) {
13     return G[i][j]=='*'?1:0;
14 }
15 
16 int main() {
17     freopen("mine.in","r",stdin);
18     freopen("mine.out","w",stdout); 
19     cin>>n>>m;
20     FOR(i,1,n) FOR(j,1,m) cin>>G[i][j];
21     FOR(i,1,n) {
22         FOR(j,1,m) {
23             if(G[i][j]=='*') cout<<'*';
24             else 
25             {
26                 int ans=A(i-1,j-1)+A(i-1,j)+A(i-1,j+1)+A(i,j-1)+A(i,j+1)+A(i+1,j-1)+A(i+1,j)+A(i+1,j+1);
27                 cout<<ans;
28             }
29         }
30         cout<<endl;
31     }
32     return 0;
33 }
View Code

 

3. 求和

                                 (sum.cpp/c/pas)

 

 

     一條狹長的紙帶被均勻划分出了 n 個格子,格子編號從 1 到 n。每個格子上都染了一種顏色(用[1,m]當中的一個整數表示),並且寫了一個數字number。𝑚𝑏𝑒𝑟𝑖

定義一種特殊的三元組:(x, y, z),其中 xyz 都代表紙帶上格子的編號,這里的三元組要求滿足以下兩個條件:

1.      𝑥, 𝑦, 𝑧都是整數, 𝑥 < 𝑦 < 𝑧, 𝑦𝑥𝑧𝑦

2.      𝑐𝑜𝑙𝑜𝑟𝑥= 𝑐𝑜𝑙𝑜𝑟𝑧

滿足上述條件的三元組的分數規定為(x + z) (𝑛𝑢𝑚𝑏𝑒𝑟𝑥+ 𝑛𝑢𝑚𝑏𝑒𝑟𝑧)。整個紙帶的分數規定為所有滿足條件的三元組的分數的和。這個分數可能會很大,你只要輸出整個紙帶的分數除以 10,007 所得的余數即可。

 

【輸入格式】

 輸入文件名為 sum.in

 第一行是用一個空格隔開的兩個正整數𝑛𝑚𝑛代表紙帶上格子的個數,𝑚代表紙帶上顏色的種類數。

 第二行有𝑛個用空格隔開的正整數,第𝑖個數字𝑛𝑢𝑚𝑏𝑒𝑟𝑖代表紙帶上編號為𝑖的格子上面寫的數字。

 第三行有𝑛個用空格隔開的正整數,第𝑖個數字𝑐𝑜𝑙𝑜𝑟𝑖代表紙帶上編號為𝑖的格子染的顏色。

 

【輸出格式】

  輸出文件名為 sum.out

共一行,一個整數,表示所求的紙帶分數除以 10,007 所得的余數。

 

【輸入輸出樣例 1

sum.in

sum.out

6 2

5 5 3 2 2 2

2 2 1 1 2 1

 

82

 

見選手目錄下的 sum/sum1.in sum/sum1.ans

 

【輸入輸出樣例 1 說明】

  紙帶如題目描述中的圖所示。

  所有滿足條件的三元組為:(1,3, 5), (4, 5, 6)

  所以紙帶的分數為(1 + 5) (5 + 2) + (4 + 6) (2 + 2) = 42 + 40 = 82

 


【輸入輸出樣例 2

sum.in

sum.out

15 4

5 10 8 2 2 2 9 9 7 7 5 6 4 2 4

2 2 3 3 4 3 3 2 4 4 4 4 1 1 1

 

1388

 

見選手目錄下的 sum/sum2.in sum/sum2.ans

 

【輸入輸出樣例 3

   見選手目錄下的 sum/sum3.in sum/sum3.ans

 

【數據說明】

 對於第 1 組至第 2 組數據,1 ≤ 𝑛 ≤ 100, 1 ≤ 𝑚 ≤ 5

 對於第 3 組至第 4 組數據,1 ≤ 𝑛 ≤ 3000, 1 ≤ 𝑚 ≤ 100對於第 5 組至第 6 組數據,1 ≤ 𝑛 ≤ 100000, 1 ≤ 𝑚 ≤ 100000,且不存在出現次數超過 20 的顏色;對於全部 10 組數據, 1 ≤ 𝑛 ≤ 100000, 1 ≤ 𝑚 ≤ 100000, 1 ≤ 𝑐𝑜𝑙𝑜𝑟𝑖𝑚, 1 ≤𝑛𝑢𝑚𝑏𝑒𝑟𝑖≤ 100000               

【思路】

  掃描法(掃描法即枚舉+維護)。

  暴力枚舉:相同顏色且相同奇偶性的兩個格子。時間為O(n^2)。

  前綴和思想應用:對於一個格子i,設之前出現的與之同奇偶p同顏色ci的格子數目為s[ci][p]、格子序號和為sr[ci][p]、格子num值之和為sa[ci][p]、格子序號與a之積之和為sq[ci][p],則如下累計ans即可:

        

  注意s>1才應該累計。

詳見代碼

【代碼】

 

 1 #include<cstdio>
 2 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 3 using namespace std;
 4 
 5 const int maxn = 100000+10;
 6 const int MOD = 10007;
 7  
 8 typedef long long LL;
 9 LL n,m;
10 LL a[maxn],c[maxn],sr[maxn][2],sa[maxn][2],s[maxn][2],sq[maxn][2];
11 //MOD用int出過錯 索性改為long long
12 
13 int main() {
14     freopen("sum.in","r",stdin);
15     freopen("sum.out","w",stdout);
16     scanf("%lld%lld",&n,&m);
17     FOR(i,1,n) scanf("%lld",&a[i]);
18     FOR(i,1,n) scanf("%lld",&c[i]);
19     LL ans=0;
20     FOR(i,1,n) {
21         int p=i&1,ci=c[i];
22         s[ci][p]++;
23         
24         if(s[ci][p]>1) 
25              ans += (a[i]*sr[ci][p])%MOD+(i*sa[ci][p])%MOD+sq[ci][p]+((s[ci][p]-1)*i*a[i]%MOD);
26         ans %= MOD;
27         
28         sr[ci][p]+=i;
29         sa[ci][p]+=a[i];
30         sq[ci][p]+=i*a[i];
31     }
32     ans%=MOD; 
33     printf("%lld\n",ans);
34     return 0;
35 }

 

4. 推銷員

                                                                (salesman.cpp/c/pas)

【問題描述】

 阿明是一名推銷員,他奉命到螺絲街推銷他們公司的產品。螺絲街是一條死胡同,出口與入口是同一個,街道的一側是圍牆,另一側是住戶。螺絲街一共有 N 家住戶,第 i 家住戶到入口的距離為 Si 米。由於同一棟房子里可以有多家住戶,所以可能有多家住戶與入口的距離相等。阿明會從入口進入,依次向螺絲街的 X 家住戶推銷產品,然后再原路走出去。   阿明每走 1 米就會積累 1 點疲勞值,向第 i 家住戶推銷產品會積累 Ai 點疲勞值。阿明是工作狂,他想知道,對於不同的 X,在不走多余的路的前提下,他最多可以積累多少點疲勞值。

【輸入格式】

  輸入文件名為 salesman.in。

  第一行有一個正整數 N,表示螺絲街住戶的數量。 

  接下來的一行有 N 個正整數,其中第 i 個整數 Si 表示第 i 家住戶到入口的距離。數據保證 S1≤S2≤…≤Sn<108。 

  接下來的一行有 N 個正整數,其中第 i 個整數 Ai 表示向第 i 戶住戶推銷產品會積累的疲勞值。數據保證 Ai<103

【輸出格式】

  輸出文件名為 salesman.out。

  輸出 N 行,每行一個正整數,第 i 行整數表示當 X=i 時,阿明最多積累的疲勞值。

【輸入輸出樣例 1】

salesman.in

salesman.out

1 2 3 4 5 

1 2 3 4 5

 

15 

19 

22 

24 

25

 

見選手目錄下的 salesman/salesman1.in 和 salesman/salesman1.ans。

【輸入輸出樣例 1 說明】

 X=1: 向住戶 5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值為 5,總疲勞值為15。 

 X=2: 向住戶 4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值為 4+5,總疲勞值為 5+5+4+5=19。

 X=3: 向住戶 3、4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值 3+4+5,總疲勞值為 5+5+3+4+5=22。

 X=4: 向住戶 2、3、4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值 2+3+4+5,總疲勞值 5+5+2+3+4+5=24。

 X=5: 向住戶 1、2、3、4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值 1+2+3+4+5,總疲勞值 5+5+1+2+3+4+5=25。

 

 

 

【輸入輸出樣例 2】

salesman.in

salesman.out

1 2 2 4 5 

5 4 3 4 1

 

12 

17 

21 

24 

27

 

見選手目錄下的 salesman/salesman2.in 和 salesman/salesman2.ans。

【輸入輸出樣例 2 說明】

 X=1:向住戶 4 推銷,往返走路的疲勞值為 4+4,推銷的疲勞值為 4,總疲勞值 4+4+4=12。

 X=2:向住戶 1、4 推銷,往返走路的疲勞值為 4+4,推銷的疲勞值為 5+4,總疲勞值4+4+5+4=17。 

 X=3:向住戶 1、2、4 推銷,往返走路的疲勞值為 4+4,推銷的疲勞值為 5+4+4,總疲勞值 4+4+5+4+4=21。

 X=4:向住戶 1、2、3、4 推銷,往返走路的疲勞值為 4+4,推銷的疲勞值為 5+4+3+4,總疲勞值 4+4+5+4+3+4=24。或者向住戶 1、2、4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值為 5+4+4+1,總疲勞值 5+5+5+4+4+1=24。

 X=5:向住戶 1、2、3、4、5 推銷,往返走路的疲勞值為 5+5,推銷的疲勞值為 5+4+3+4+1,總疲勞值 5+5+5+4+3+4+1=27。

【樣例輸入輸出 3】

見選手目錄下的 salesman/salesman3.in 和 salesman/salesman3.ans。

 

【數據說明】對於 20%的數據,1≤N≤20;  對於 40%的數據,1≤N≤100;  對於 60%的數據,1≤N≤1000;  對於 100%的數據,1≤N≤100000。

 

【思路】

  貪心+線段樹。

  可以知道每次增加人數得到的答案都是在前一個答案的基礎上挑選了一家最優的客戶。貪心地看,我們每次應該選擇勞累度增加值最大的。對於每一次查詢而言,設目前為止出現的最遠客戶的下標為srank,同時定義sa[i]=2*s[i]+a[i],我們在srank之左的客戶中選出a值最大的,在srank之右選出sa值最大的,將sa減去2*s[srank]之后比較得當前最大增加值,並相應設最大值為0。以上操作均可以通過維護兩棵線段樹在O(logn)時間內完成,因此總的時間為O(nlogn)。

 (應該會有更簡單的方法)

【代碼】

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
  5 using namespace std;
  6 
  7 const int maxn = 400000+10;
  8 const int INF=1e9;
  9 
 10 struct Node{
 11     int mx,mr;
 12     Node() { mx=-INF; }
 13 };
 14 struct Segment_Tree {
 15     int n;
 16     int t[maxn],maxv[maxn],maxr[maxn];
 17     
 18     void build(int u,int L,int R) {
 19         int lc=u*2,rc=u*2+1;
 20         if(L==R) {
 21             maxv[u]=t[L];
 22             maxr[u]=L;
 23         }
 24         else {
 25             int M=L+(R-L)/2;
 26             build(lc,L,M);
 27             build(rc,M+1,R);
 28             if(maxv[lc]>maxv[rc]) {
 29                 maxv[u]=maxv[lc];
 30                 maxr[u]=maxr[lc];
 31             }
 32             else {
 33                 maxv[u]=maxv[rc];
 34                 maxr[u]=maxr[rc];
 35             }
 36         }
 37     }
 38     void init(int n,int*a) {
 39         this->n=n;
 40         FOR(i,1,n) t[i]=a[i];
 41         build(1,1,n);
 42     }
 43     int y1,y2;
 44     Node query(int u,int L,int R) {
 45         int lc=u*2,rc=u*2+1;
 46         if(y1<=L && R<=y2) {
 47             Node ans; 
 48             ans.mr=maxr[u],ans.mx=maxv[u];
 49             return ans;
 50         }
 51         else {
 52             int M=L+(R-L)/2;
 53             Node ans,tmp;
 54             if(y1<=M) {
 55                 tmp=query(lc,L,M);
 56                 if(tmp.mx>ans.mx) ans=tmp;
 57             }
 58             if(M<y2) {
 59                 tmp=query(rc,M+1,R);
 60                 if(tmp.mx>ans.mx) ans=tmp;
 61             }
 62             return ans;
 63         }
 64     }
 65     int x;
 66     void Add(int u,int L,int R) {
 67         int lc=u*2,rc=u*2+1;
 68         if(L==R) {
 69             maxv[u] = 0;
 70             maxr[u] = L;
 71         }
 72         else {
 73             int M=L+(R-L)/2;
 74             if(x<=M) Add(lc,L,M);
 75             else Add(rc,M+1,R);
 76             if(maxv[lc]>maxv[rc]) {
 77                 maxv[u]=maxv[lc];
 78                 maxr[u]=maxr[lc];
 79             }
 80             else {
 81                 maxv[u]=maxv[rc];
 82                 maxr[u]=maxr[rc];
 83             }
 84         }
 85     }
 86 }A,B;
 87 
 88 int s[maxn],sa[maxn],sb[maxn];
 89 int n;
 90 
 91 int read() {
 92     char c=getchar();
 93     while(!isdigit(c)) c=getchar();
 94     int x=0;
 95     while(isdigit(c)) {
 96         x=x*10+c-'0';
 97         c=getchar();
 98     }
 99     return x;
100 }
101 int main() {
102     freopen("salesman.in","r",stdin);
103     freopen("salesman.out","w",stdout);
104     
105     n=read();
106     FOR(i,1,n) s[i]=read();
107     FOR(i,1,n) sb[i]=read();
108     FOR(i,1,n) sa[i]=2*s[i]+sb[i];
109     
110     A.init(n,sa),B.init(n,sb);
111     
112     int srank=0,ans=0;
113     FOR(i,1,n) {
114         int a,b;
115         if(srank==0) a=0,b=1;
116         else a=srank-1,b=srank+1;
117         Node t1,t2;
118         if(a) {
119             B.y1=1,B.y2=a;
120             t1=B.query(1,1,n);
121         }
122         if(b<=n) {
123             A.y1=b,A.y2=n;
124             t2=A.query(1,1,n);
125             t2.mx=sb[t2.mr]+2*(s[t2.mr]-s[srank]);
126         }
127         int ansl=0,ansr=0;
128         if(t1.mx!=-INF)  ansl=sb[t1.mr];
129         if(t2.mx!=-INF)  ansr=t2.mx;
130         Node res;
131         if(ansl>ansr) res=t1; else res=t2,srank=res.mr;
132         ans += res.mx;
133         printf("%d\n",ans);
134         A.x=B.x=res.mr;
135         A.Add(1,1,n),B.Add(1,1,n);
136     }
137     return 0;
138 } 

 

ps:=-=

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM