T1 金幣
題目描述
國王將金幣作爲工資,發放給忠誠的騎士。第一天,騎士收到一枚金幣;之後兩天(第二天和第三天),每天收到兩枚金幣;之後三天(第四、五、六天),每天收到三枚金幣;之後四天(第七、八、九、十天),每天收到四枚金幣……;這種工資發放模式會一直這樣延續下去:當連續 天每天收到 枚金幣後,騎士會在之後的連續 天裏,每天收到 枚金幣。
請計算在前 天裏,騎士一共獲得了多少金幣。
輸入格式
輸入只有 行,包含一個正整數 ,表示發放金幣的天數。
輸出格式
輸出只有 行,包含一個正整數,即騎士收到的金幣數。
數據範圍
對於 的數據,。
樣例說明
樣例1:
騎士第一天收到一枚金幣;第二天和第三天,每天收到兩枚金幣;第四、五、六天,每天收到三枚金幣。因此一共收到 枚金幣。
T1分析
送分題,按照題目要求一天一天模擬,兩層for
循環就可以了,第一層枚舉個數,第二層循環枚舉所有的數字。
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
long long sum = 0;
for(int i = 0; ;i++){
for(int j = 0; j < i; j++){
sum += i;
n--;
if(n == 0){
cout << sum << endl;
return 0;
}
}
}
return 0;
}
T2 掃雷遊戲
題目描述
掃雷遊戲是一款十分經典的單機小遊戲。在 行 列的雷區中有一些格子含有地雷(稱之爲地雷格),其他格子不含地雷(稱之爲非地雷格)。玩家翻開一個非地雷格時,該格將會出現一個數字——提示周圍格子中有多少個是地雷格。遊戲的目標是在不翻出任何地雷格的條件下,找出所有的非地雷格。
現在給出 行 列的雷區中的地雷分佈,要求計算出每個非地雷格周圍的地雷格數。
注:一個格子的周圍格子包括其上、下、左、右、左上、右上、左下、右下八個方向上與之直接相鄰的格子。
輸入格式
輸入文件第一行是用一個空格隔開的兩個整數 和 ,分別表示雷區的行數和列數。
接下來 行,每行 個字符,描述了雷區中的地雷分佈情況。字符’*’
表示相應格子是地雷格,字符’?’
表示相應格子是非地雷格。相鄰字符之間無分隔符。
輸出格式
輸出文件包含 行,每行 個字符,描述整個雷區。用’*’
表示地雷格,用周圍的地雷個數表示非地雷格。相鄰字符之間無分隔符。
數據範圍
對於 的數據,。
T2分析
簡單模擬題,其實也是送分題,每次找到一個地雷之後,把他周圍的所有數字全部 就可以了
#include <iostream>
using namespace std;
const int N = 1e2 + 9;
char a[N][N];
int f[N][N];
int dx[] = {1, -1, 0, 0, 1, -1, 1, -1};
int dy[] = {0, 0, -1, 1, 1, -1, -1, 1};
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
if (a[i][j] == '*') {
for (int k = 0; k < 8; k++) {
int x = i + dx[k];
int y = j + dy[k];
f[x][y]++;
}
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == '*') {
cout << '*';
} else {
cout << f[i][j];
}
}
cout << endl;
}
return 0;
}
T3 求和
題目描述
一條狹長的紙帶被均勻劃分出了 個格子,格子編號從 到 。每個格子上都染了一種顏色 用 當中的一個整數表示),並且寫了一個數字 。
定義一種特殊的三元組:,其中 都代表紙帶上格子的編號,這裏的三元組要求滿足以下兩個條件:
-
是整數,
-
滿足上述條件的三元組的分數規定爲 。整個紙帶的分數規定爲所有滿足條件的三元組的分數的和。這個分數可能會很大,你只要輸出整個紙帶的分數除以 所得的餘數即可。
輸入格式
第一行是用一個空格隔開的兩個正整數 和 表紙帶上格子的個數, 表紙帶上顏色的種類數。
第二行有 用空格隔開的正整數,第 數字 表紙帶上編號爲 格子上面寫的數字。
第三行有 用空格隔開的正整數,第 數字 表紙帶上編號爲 格子染的顏色。
輸出格式
共一行,一個整數,表示所求的紙帶分數除以 所得的餘數。
數據範圍
對於第 組至第 組數據,,;
對於第 組至第 組數據,,;
對於第 組至第 組數據,,,且不存在出現次數超過 的顏色;
對於全部 組數據,,,,。
樣例說明
樣例1:
紙帶如題目描述中的圖所示。
所有滿足條件的三元組爲:,。
所以紙帶的分數爲 。
T3分析
觀察數據範圍,首先前兩組 所以直接 暴力即可獲得 分
繼續觀察題目要求,可以看到首先我們要滿足第一個條件 ,左右移動後可以得到式子 從這裏我們可以知道, 必然是一個偶數,而 那就意味着 要麼同是奇數要麼同是偶數,當然如果不理解這個其實自己舉個例子也就能發現了
繼續往後看可以發現,我們所需要計算的貢獻是跟 無關的,所以第一個式子可以得到一個結論, 是同奇同偶的,在這個條件下我們可以發現,任意兩個奇偶性相同的 必然存在一個 使得 ,所以也就意味着只要奇偶性相同的兩個數字 就一定可以滿足 第一個條件
在有了這個條件後,其實 的分數就可以獲得了,只要暴力枚舉 即可獲得 的分數
接下來看第二個條件,也就是 ,也就是說只要 奇偶性相同,並且顏色相同,那這組 就可以作爲一組三元組的解,將貢獻加到答案中
其實 的數據也在提示這一點,接下來的考慮方向應該是顏色!
所以我們可以先將紙帶進行奇偶的區分,將奇數部分和偶數部分單獨取出,對於兩部分分別取出顏色相同的所有元素,這部分所有元素就是可以任意兩兩組合成合法三元組的 ,那我們現在要做的就是計算這組元素的貢獻和
假設我們取出一組元素,比如取出一組同一個顏色的奇數元素,將這組元素的下標看做 這組元素的 看做 ,任意兩兩組合計算貢獻,可以得到如下式子
化簡後的結果就是快速計算這組貢獻和的方法,所以這裏只需要在分別討論奇偶的情況下,記錄下同一個顏色的前綴下標和,前綴數字和,前綴數字乘下標的和,即可 計算出結果
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
using namespace std;
long long n,m,num[100005],cont[2][100005],cl;
long long sum1[3][100005],sum2[3][100005];
long long ans;
int main(){
scanf("%lld%lld",&n,&m);
for(int i = 1; i <= n; i++){
scanf("%lld",&num[i]);
}
for(int i = 1; i <= n; i++){
scanf("%lld",&cl);
if(i % 2 == 1){
ans = (ans + sum1[0][cl] % 10007 + i * sum1[1][cl] % 10007 + num[i] * sum1[2][cl] % 10007 + cont[0][cl] * i * num[i] % 10007) % 10007;
sum1[0][cl] = (sum1[0][cl] + num[i]*i) % 10007;
sum1[1][cl] = (sum1[1][cl] + num[i]) % 10007;
sum1[2][cl] = (sum1[2][cl] + i) % 10007;
cont[0][cl]++;
} else {
ans = (ans + sum2[0][cl] % 10007 + i * sum2[1][cl] % 10007 + num[i] * sum2[2][cl] % 10007 + cont[1][cl] * i * num[i] % 10007) % 10007;
sum2[0][cl] = (sum2[0][cl] + num[i] * i) % 10007;
sum2[1][cl] = (sum2[1][cl] + num[i]) % 10007;
sum2[2][cl] = (sum2[2][cl] + i) % 10007;
cont[1][cl]++;
}
}
printf("%lld",ans % 10007);
}
T4 推銷員
題目描述
阿明是一名推銷員,他奉命到螺絲街推銷他們公司的產品。螺絲街是一條死衚衕,出口與入口是同一個,街道的一側是圍牆,另一側是住戶。螺絲街一共有 家住戶,第i家住戶到入口的距離爲 米。由於同一棟房子裏可以有多家住戶,所以可能有多家住戶與入口的距離相等。阿明會從入口進入,依次向螺絲街的 家住戶推銷產品,然後再原路走出去。
阿明每走 米就會積累 點疲勞值,向第 家住戶推銷產品會積累 點疲勞值。阿明是工作狂,他想知道,對於不同的 ,在不走多餘的路的前提下,他最多可以積累多少點疲勞值。
輸入格式
第一行有一個正整數 ,表示螺絲街住戶的數量。
接下來的一行有 個正整數,其中第 個整數 表示第 家住戶到入口的距離。數據保證 。
接下來的一行有 個正整數,其中第 個整數 表示向第i戶住戶推銷產品會積累的疲勞值。數據保證 。
輸出格式
輸出 行,每行一個正整數,第i行整數表示當 時,阿明最多積累的疲勞值。
數據範圍
對於 的數據,;
對於 的數據,;
對於 的數據,;
對於 的數據,。
樣例說明
樣例1:
: 向住戶 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值爲 。
: 向住戶 、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值爲 。
: 向住戶 、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值 ,總疲勞值爲 。
: 向住戶 、、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值 ,總疲勞值 。
: 向住戶 、、、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值 ,總疲勞值 。
樣例2:
:向住戶 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。
:向住戶 、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。
:向住戶 、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。
:向住戶 、、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。或者向住戶 、、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。
:向住戶 、、、、 推銷,往返走路的疲勞值爲 ,推銷的疲勞值爲 ,總疲勞值 。
T4分析
前 分的數據有各種奇怪的做法…都可以
注意這裏有句話 不走多餘的路,所以簡單分析一下題目就可以得到題意: 選 的貢獻其實就是對於我們所選的 家,總的疲勞值是
那麼正過來想,對於 的時候必然是選最大的
那麼接着往下, 其實無非就是在 的前提下,再選一家推銷,而這裏可以發現,如果上一次選的是 ,這次選 則可以發現,如果選在上一次選的左邊,即 那貢獻只有 而如果選在右邊即 ,貢獻則是
而 的最大值必然存在於這兩種情況裏,所以由此可以推導出,每一步都可以這樣來選
當然也可以倒過來驗證這個思想,對於 時,必然是所有人都選,在這個前提下考慮
我們當然是去掉貢獻最小的,就會得到 時的最大貢獻值,繼續往下每次減去最小影響的那戶人即可
所以這個貪心思路無疑是沒有問題的,那麼接下來就是如何實現的問題了,對於現在處於第 家時,我們可以發現,對於 的所有住戶只要求出最大的 即可這一步我們完全可以用一個優先隊列或者堆來維護
而右邊部分我們要維護的則是 ,每次在兩種情況中選取最大值即可
當然這裏存在一種特殊情況,那就是如果左邊和右邊兩邊對當前這一步的貢獻值一樣,該選哪一邊?
其實取取誰都一樣,這個很容易證明,我們當前處於 ,現在有 和 同時滿足當前貢獻最大的情況,那麼我們這一步如果選 ,那必然會使得 的影響變得更大,因爲距離更遠了,所以我們下一步必然會選 ,而如果這一步選的是 , 本就是左邊 中最大的,雖然選取 後會使得處於左邊的數變多,但是原本在 右邊的點在額外附加了 之後的 都沒有 大,那就算點變多了,左邊最大的依舊是 ,所以下一步必然會選擇
這裏如果右邊的點不使用優先隊列維護的話,時間複雜度最好情況下是 ,最壞情況下是 看數據,有可能因爲常數部分會被卡超時,如果數據較爲平均則能卡過去
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int n, s[maxn], a[maxn], curr, maxL, ans;
struct Node{
int i, v;
bool operator < (const Node& a)const{
return v < a.v;
}
}node,maxR;
priority_queue<Node>qR;
priority_queue<int>qL;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &s[i]);
}
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
for(int i = 1; i <= n; i++){
node.i = i;
node.v = 2 * s[i] + a[i];
qR.push(node);
}
for(int x = 1; x <= n; x++){
maxL = maxR.v = 0;
if(!qL.empty()){
maxL = qL.top();
}
while(!qR.empty() && qR.top().i <= curr){
qR.pop();
}
if(!qR.empty()){
maxR=qR.top();
}
if(maxL < maxR.v - 2 * s[curr]){//當兩者相等時取哪邊都一樣
ans += maxR.v - 2 * s[curr];
for(int k = curr + 1; k < maxR.i; k++){
qL.push(a[k]);
}
curr = maxR.i;
qR.pop();
} else {
ans += maxL;
qL.pop();
}
printf("%d\n",ans);
}
return 0;
}