第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽(同步賽)
技術標籤:個人賽題解
B-小寶的幸運陣列
題目描述
對於小寶來說,如果一個數組的總和能夠整除他的幸運數字k,就是他的幸運陣列,而其他陣列小寶都很討厭。現在有一個長度為n的陣列,小寶想知道這個陣列的子陣列中,最長的幸運子陣列有多長。
對於子陣列的定義,如果可以通過從開頭和從結束分別刪除若干個(可以為零或全部,前後刪除個數不必相同)元素來從陣列b獲得陣列a,則稱陣列a是陣列b的子陣列。(子陣列包含原陣列,但不包含空串)
題意:
對一個數組求陣列和能整除k的子陣列的個數,子陣列是指原陣列中任意連續的陣列,長度大於0即可。
思路:
暴力解法是兩重for迴圈對字首和求差,找最長的。但會TLE。所以進行一下改進:觀察題意要的是能整除k的子陣列,所以可以讓字首和對k取模,這樣如果模相等,則求差的時候得到的中間的陣列和就可以整除k,只需要找到最大的長度即可。方法是利用兩個數字分別存每個取模以後的最大和最小的位置,最後進行一次迴圈輸出最大減最小的差即可。
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
ll tr[100005], ma[100005], mi[100005], have[100005];
ll x, t, n, k, a;
int main()
{
cin>>t;
while(t--)
{
a = 0;//a用來判斷最終結果是否輸出-1
memset(tr, 0, sizeof(tr));//初始化
memset(ma, 0, sizeof(ma)) ;//ma陣列用來存每一個餘數出現的最大位置,因為是求max,則需讓陣列初始化為-1e9,但本題的值都大於等於0,所以沒必要賦-1e9
memset(have, 0, sizeof(have));//have陣列用來存每一個餘數出現的次數,只有出現次數大於1,其相減才能得到能整除k的子陣列
for(int i = 0; i < 100005; i++)
mi[i] = inf;//mi陣列用來存每一個餘數出現的最小位置,因為是求min,所以賦初始值為最大的數
mi[0] = 0;
/*這個和下面的那個初始化很重要,因為可能是字首和本身就能整除k,此時餘數就是0,而這個時候不需要兩個的差,所以要給have[0] = 1,這樣0這個位置就有資格去求了,而且mi[0] = 0,不會出現比0還小的位置,減的時候就正確*/
have[0] = 1;
cin>>n>>k;
for(int i = 1; i <= n; i++){
cin>>x;
tr[i] = tr[i - 1] + x;//求字首和
}
for(ll i = 1; i <= n; i++){
tr[i] = tr[i] % k;//對字首和取模
mi[tr[i]] = min(mi[tr[i]], i);//存餘數出現的最小位置
ma[tr[i]] = max(ma[tr[i]], i);//存餘數出現的最大位置
have[tr[i]]++;
if(have[tr[i]] >= 2)
a = 1;
}
if(a == 0)
cout<<-1<<endl;
else{
ll ans = 0;
for(ll i = 0; i < 100005; i++){
if(have[i] >= 2)
ans = max(ans, ma[i] - mi[i]);
}
cout<<ans<<endl;
}
}
return 0;
}
C-上進的凡凡
題目描述
凡凡是一個上進的人,他的人生沒有下坡路,他也討厭帶有”下坡路“的東西。
所以,對於凡凡來說,只有非降序的陣列才是nice的(如:1,2,2,3,4,5,5);若陣列元素個數為1,也滿足非降序,也是nice的。
現在有一個長度為n的陣列,凡凡想知道它的子陣列中有多少個數組是nice的。
題意:
讓你求非降序的子陣列的個數,相等也算非降序。
思路:
先舉個栗子:1,2,3,4,3,1,2從1開始看,子陣列有[1];再看2,它和1構成非降序,所以子陣列又多了[1,2]和[2];再看3,它和1,2構成非降序,所以子陣列又多了[1,2,3]和[2,3]和[3];再看4,它和1,2,3構成非降序,所以字陣列又多了[1,2,3,4]和[2,3,4]和[3,4]和[4];再看第二個3,他和前面的不構成非降序,所以子陣列又多了個[3];再看第2個1,他和前面的也不構成非降序,所以子陣列又多了[1];再看第2個2,他和前面的構成降序,所以子陣列又多了[1,2]和[2];所以子陣列的數量是1 + 2 + 3 + 4 + 1 + 1 + 2。規律就是每多一個非降序的數,則加的值是上次加的值加一,而每多一個降序的數,就加1。還有一種做法是將陣列分成若干個非降序的子陣列,然後利用(n + 1) * n / 2的公式帶入所有的子陣列的長度然後相加,如果是降序的就1個1個的分開。這個公式其實也就是我上面的那個規律的等差求和公式而已,不過我個人感覺比較麻煩,你每次都得找兩個端點才能套公式,而我的那個只需要一次模擬,時時更新值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, tr[100005];
int main()
{
cin>>n;
for(int i = 1; i <= n; i++)
cin>>tr[i];
tr[0] = 1e9;
ll sum = 0, k = 1;
for(int i = 1; i <= n; i++)
{
if(tr[i] >= tr[i - 1])
{
k++;
}
else
k = 1;
sum += k;
}
cout<<sum<<endl;
return 0;
}
Seek the Joker I
題目描述
長達數日的春日祭終於告一段落,作為巫女的朝野芳乃在打掃完神社本決定好好享受一下久違的寧靜。然而守護了神刀數百年的叢雨難耐寂寞,希望芳乃能陪她一起玩撲克消解愁悶。
芳乃並不擅長市井的遊戲,所以總是輸多贏少。而昨日被芳乃的神樂舞深深吸引,以致一早就前來建實神社希望能再睹芳華的你碰巧聽見了此事。儘管不知道為什麼美麗的巫女要自言自語地為玩撲克而苦惱,但你毅然決然地毛遂自薦,希望能為芳乃一解眉間愁。
芳乃告訴了你叢雨準備了n張撲克牌作為牌堆,每人每次至多從牌堆頂部抽k張牌,至少抽1張牌。牌堆底部的最後一張牌作為烏龜,抽中的將輸掉這一輪比賽。芳乃想知道在你的幫助下,她和叢雨都採取積極策略時,她自己是否一定能獲勝。作為被叢雨邀請的一方,每輪遊戲都是芳乃先抽。
因為看不見叢雨而誤認芳乃罹患精神分裂的你在不由感嘆紅顏薄命的同時,決定盡全力幫助芳乃完成她的委託。
宣告:本題中的所有角色在劇情發生時均已超過18歲。
題意:
芳乃和從雨玩遊戲。n張牌,每人最多每次最多取k張牌,至少取一張,抽到最後一張牌的人輸,芳乃先抽。
芳乃贏輸出“yo xi no forever!”否則輸出”ma la se mi no.1!“
思路:
簡單的巴什博弈,之前是n張牌,取完最後一張的贏,本題只需讓n-1即可得到裸的巴什博弈。判(n - 1)%(k + 1),如果等於0,先手輸,否則先手必勝!
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, k, t;
cin>>t;
while(t--){
cin>>n>>k;
if((n - 1) % (k + 1) == 0)
cout<<"ma la se mi no.1!\n";
else
cout<<"yo xi no forever!\n";
}
return 0;
}
E-Seek the Joker II
題目描述
長達數日的春日祭終於告一段落,作為巫女的朝野芳乃在打掃完神社本決定好好享受一下久違的寧靜。然而守護了神刀數百年的叢雨難耐寂寞,希望芳乃能陪她一起玩撲克消解愁悶。
芳乃並不擅長市井的遊戲,所以總是輸多贏少。而昨日被芳乃的神樂舞深深吸引,以致一早就前來建實神社希望能再睹芳華的你碰巧聽見了此事。儘管不知道為什麼美麗的巫女要自言自語地為玩撲克而苦惱,但你毅然決然地毛遂自薦,希望能為芳乃一解眉間愁。
芳乃告訴了你叢雨準備了n張撲克牌作為牌堆,自牌頂向下數第x張牌作為烏龜,即“烏龜”的上方有x-1張牌,“烏龜”的下方有n-x張牌,抽中“烏龜”的將輸掉這一輪比賽。每人每次可以同時在牌堆頂和牌堆底或者僅在牌堆頂或牌堆底其抽取任意張牌,至少抽1張牌。但若選擇同時在牌堆頂和牌堆底抽牌,則抽牌數量需要相同。芳乃想知道在你的幫助下,她和叢雨都採取積極策略時,她自己是否一定能獲勝。作為被叢雨邀請的一方,每輪遊戲都是芳乃先抽。
因為看不見叢雨而誤認芳乃罹患精神分裂的你在不由感嘆紅顏薄命的同時,決定盡全力幫助芳乃完成她的委託。
宣告:本題中的所有角色在劇情發生時均已超過18歲。
題意:
n張牌,抽走第x張牌的輸,第x張牌叫烏龜,烏龜上面有x-1張牌,烏龜下面有n-x張牌。每人每次抽排有兩種方法,一是在上面或下面抽大於0的任意張牌,二是同時在上面和下面抽相同數目張牌。
思路:
威佐夫博弈。
我現在將這個題換個描述方式:有兩堆牌,一堆有n-x張牌,另一堆有x-1張牌,取完最後一張的人贏。(因為取完最後一張,對手就可以取那張為在兩堆牌內的烏龜了,所以你就贏了)
現在就很明顯了,是威佐夫博弈,具體解釋看我關於博弈論的一篇部落格(雖然我就寫了一半,懶得寫下去了……手動狗頭。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t, x, n;
cin>>t;
while(t--){
cin>>n>>x;
int a = x - 1;
int b = n - x;
if(a > b)
swap(a, b);
if((int)((b - a) * (sqrt(5.0) + 1.0) / 2) == a)
cout<<"ma la se mi no.1!\n";
else
cout<<"yo xi no forever!\n";
}
return 0;
}
F-成績查詢ing
題目描述
去年的新冠疫情爆發讓眾多大學生只能只能在家裡上學,老師為了方便自己錄入成績和方便大家成績查詢,建立了一個錄入和查詢成績的系統,能完成M次兩種不同的查詢,輸入查詢次數M,查詢M次,每次首先輸入查詢的模式T,T為1時,輸入同學的姓名Name,並依次輸出同學的成績Grade(0<=Grade<=100), 學號(0~1000000},性別(1/2),T為2時,輸入成績,輸出有具體有哪些同學考到了這個分數,輸出同學的Name,並要求按字典序輸出,當沒有同學為此分數時,則不輸出。字典序,對於字串,先按首字元排序,如果首字元相同,再按第二個字元排序,以此類推。
題意:
先錄入學生的姓名成績性別學號。然後進行m次查詢,查詢有兩種情況,一種是輸入姓名,輸出成績學號性別;另一種是輸入成績,輸出有這個成績的所有學生的名字,按字典序輸出。
思路:
用一個結構體存成績學號性別,通過map的結構體解決情況1,再用set的陣列解決情況2.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ran
{
int grade, gender, id;
}tr;
map<string, ran>mp;
set<string>se[120];
string s;
int main()
{
int n;scanf("%d",&n);
for(int i = 1; i <= n; i++){
cin>>s;
scanf("%d%d%d",&tr.grade, &tr.gender, &tr.id);
mp[s] = tr;
se[tr.grade].insert(s);
}
int m; scanf("%d",&m);
while(m--){
int t;scanf("%d",&t);
if(t == 1){
cin>>s;
printf("%d %d %d\n",mp[s].grade,mp[s].id,mp[s].gender);
}
else{
int x;scanf("%d",&x);
for(auto y : se[x]){//用迭代器會超時,用auto不會
cout<<y<<'\n';
}
}
}
return 0;
}
H-數羊
題目描述
憨憨小楊晚上睡不著覺,就開始數羊,她覺得一隻一隻數太慢了,突發奇想出了一種新的數羊方式,羊羊數量A(n,m)由兩個整形變數n和m決定,計算方式如下:
現在給出n和m的值,請你幫小楊數數一共有多少隻羊。
思路:
直接暴力列舉前十項,你就會發現規律
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int IntRead()
{
char ch = getchar();
int s = 0, w = 1;
while(ch < '0' || ch > '9')
{
if(ch == '-')
w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0',
ch = getchar();
}
return s * w;
}
//int f(int x, int y)
//{
// if(x == 1 && y == 0)
// {
// tr[x][y] = 2;
// return tr[x][y];
// }
// else if(y >= 0 && x == 0)
// {
// tr[x][y] = 1;
// return tr[x][y];
// }
// else if(y == 0 && x >= 2)
// {
// tr[x][y] = x + 2;
// return tr[x][y];
// }
// else if(x >= 1 && y >= 1)
// {
// tr[x][y] = f(f(x - 1, y),y - 1);
// return tr[x][y];
// }
// // return f(f(n - 1),m-1);
//}
ll q_pow(ll a, ll b)//第三列是2的n次方,所以要用快速冪取模
{
ll ans = 1;
while(b > 0)
{
if(b & 1)
{
ans = ans * a %998244353;
}
a = a * a % 998244353;
b >>= 1;
}
return ans % 998244353;
}
int main()
{
ll t, n, m;
t = IntRead();
while(t--)
{
n = IntRead();
m = IntRead();
if(m == 0)
{
if(n == 0)
printf("1\n");
else if(n == 1)
printf("2\n");
else
printf("%ld\n",n + 2);
}
else if(m == 1)
{
if(n == 0)
printf("0\n");
else
printf("%ld\n", 2 * n);
}
else if(m == 2)
{
if(n == 0)
printf("0\n");
else
printf("%ld\n",q_pow(2,n));
}
}
return 0;
}
I-買花
題目描述
情人節馬上要到了,陽陽想送出n朵花給喜歡的妹妹,他打算提前開始買。但是,因為他有強迫症,所有的花要分k天買(k>1,即不能一天全買完),第一天他可以買任意朵花,之後每一天買花的數量為前一天的兩倍,(如若第一天買4朵,第二天就要買8朵,以此類推)。
現在離情人節還有15天(k≤15),請你告訴陽陽,他能不能剛好買到n朵花。
題意:
要買n朵花,規則是第一天買任意朵,然後第二天比第一天多買一倍,最多買15天,花數是所有花的總和,問你能不能剛好買到n朵花。
思路:
買1朵的時候,花數f對於天數的函式為f(x) = 2 ^ x - 1.
所以我們只需要對n進行一次迴圈,看看能不能除盡這十五個數,如果可以就行,否則就不行。
這個題的輸出我真的想噴,YES寫成YE5,NO寫成N0,這不認真看誰看的出來啊!這要是wa了就是在搞心態!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll tr[100];
int main()
{
ll t, n, x;
cin>>t;
while(t--)
{
x = 0;
cin>>n;
for(int i = 1; i <= 15; i++)
{
tr[i] = pow(2,i) - 1;
}
for(int i = 2; i <= 15; i++)
{
if(n % tr[i] == 0)
{
x = 1;
// cout<<tr[i]<<endl;
break;
}
}
if(x)
cout<<"YE5\n";
else
cout<<"N0\n";
}
return 0;
}
K-黑洞密碼
題目描述
近些日子,某科學家接受到了來自外太空的神祕訊息,在經過了一段時間的研究後,科學家發現訊息是一個由字母和數字組成的字串str,想要破譯,需要通過一定的規則將字串進行轉換。規則如下:
1.確定訊息的長度為32;
2.字串中第4n+1∼\sim∼4n+4的字母和第4n+14n+4的字母和第4n+14n+4的字母和第4n+1∼\sim∼4n+4(4n+4(4n+4(0≤n≤30 \leq n \leq 30≤n≤3)的數字為一組,共4組;
3.每組的第1,2,3,4個字元分別往後推每組第1,2,3,4個數字個數 例:如第一個字母為a,第一個數字為3,轉換後變為d,‘z’之後是’B’,‘Z’之後是’b’;
4.將每組內部字母的順序顛倒;
5.將四組字符合並就是最後的訊息。
思路:
四個一組進行處理就可以,反正也不長,怎麼搞都行
#include<bits/stdc++.h>
using namespace std;
char f(char x, int y)//這是轉變字元的函式
{
if(x + y > 122)//看看是不是超過了z
{
x = 65 + y - (122 - x);
return (char)x;
}
else if(x + y > 90 && x <= 90)//看看是不是超過了Z,注意要判斷x<=90,因為Z與a就隔了7個字元,而y是屬於[0,9]的,所以可能會有問題
{
x = 97 + y - (90 - x);
return (char)x;
}
else
{
x += y;
return (char)x;
}
}
int main()
{
int tr[20];
char ar[20];
string s;
cin>>s;
int a = 0, b = 0;
for(int i = 0; i <= s.size(); i++)
{
if((s[i] >= 65 && s[i] <= 90) || (s[i] >= 97 && s[i] <= 122))
ar[++a] = s[i];
else
tr[++b] = s[i] - '0';
}
for(int i = 4; i >= 1; i--)
{
cout<<f(ar[i],tr[i]);
}
for(int i = 8; i >= 5; i--)
{
cout<<f(ar[i],tr[i]);
}
for(int i = 12; i >= 9; i--)
{
cout<<f(ar[i],tr[i]);
}
for(int i = 16; i >= 13; i--)
{
cout<<f(ar[i],tr[i]);
}
cout<<'\n';
return 0;
}
剩下的三個題,一個是A題切蛋糕,看題幹感覺好長,就真懶得讀
還有一個G題感覺是個“博弈”,但是又不太像博弈,有點思路但又感覺不太對,先放放
最後一個是L題,好像是個建圖的題,沒學過,等有空回來再補吧