Table of Contents
前言
首先聲明,本篇博客爲根據對賽時的回憶所寫,相對於“題解”,其實更偏向於“總結”,總結賽時的機智和失誤。本博客提到的做法均不一定正確,請帶着批判的眼光來讀,發現問題歡迎評論與我討論。
題目&做法
第一題
題意:
貌似是求1~2019的數裏含有'2','0','1','9'的數的平方和?
做法:
C++11有to_string,把每個數轉成字符串之後判斷每一位是否爲那些數,如果是,加到平方和。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 2019;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
const char tmp[] = {'2','0','1','9'};
int main(){
redirect();
ll sum = 0;
for(int i = 1;i <= maxn;i++){
string str = to_string(i);
for(int j = 0;str[j];j++)
for(int k = 0;k < 4;k++)
if(str[j] == tmp[k]){
sum += i*i;
goto label;
}
label:
continue;
}
printf("%lld\n",sum);
return 0;
}
總結:
賽後寫的代碼,跟賽時寫得不太一樣。。我記得我沒有寫label。。那麼我賽時可能5分送分題寫掛了?可我記得樣例過了啊。。不太清楚,可能是記錯題了。
第二題
題意:
貌似是類斐波那契數列?只不過遞推式改爲了f[i] = f[i-1] + f[i-2] + f[i-3]。只問f[20190324]最後四位。
思路:
直接遞推就可以,數又不大。每次求完對10000取模即可,n再大考慮矩陣快速冪?
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = 10000;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
int f[20190325];
int main(){
redirect();
f[1] = f[2] = f[3] = 1;
for(int i = 4;i <= 20190324;i++)f[i] = (f[i-1] + f[i-2] + f[i-3])%mod;
printf("%d\n",f[20190324]);
return 0;
}
總結:
答案4659,忘記我答案多少了。。希望我沒有從f[0]開始而多跑一位。。。
第三題
題意:
把1~49分成7組,求每一組中位數,7箇中位數再求一箇中位數,問最終結果最大多少。
思路:
貪心,一定比這個中位數大的就讓比它大,剩下的都比它小就好。這個答案應該在分組的第四組,一定比它大的包括第五、六、七組中位數以及大於這些中位數的數:
(1,1) (1,2) (1,3) (1,4) (1,5) (1,6) (1,7)
(2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7)
(3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7)
(4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7)
(5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7)
(6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7)
(7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7)
我們要讓(4,4)最大,比它大的有(4,5)~(4,7) (5,4)~(5,7) (6,4)~(6,7) (7,4)~(7,7)共有3*4+3個數,49-(3*4+3)=34
總結:
賽場上智障了,莫名其妙以爲(5,4)是要求的答案,求出來個結果38。涼了。
第四題
題意:
給一個迷宮,求在步數最小的情況下,取行動方案連成的字典序最小的字符串。
思路:
按字典序排每一次試探的行動順序,然後就是bfs走迷宮模板題,將步數變成字符串就行。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
static const char ch[] = {'D','L','R','U'};
static const int dx[] = {1,0,0,-1};
static const int dy[] = {0,-1,1,0};
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
struct node{
int x,y;
string z;
node(int a,int b,string c){
x = a,y = b,z = c;
}
node(){}
};
int maze[100][100];
bool vis[100][100];
int n = 30,m = 50;
int sx,sy,gx,gy;
int main(){
redirect();
for(int i = 0;i < n;i++)
for(int j = 0;j < m;j++)
scanf("%1d",&maze[i][j]);
gx = n-1,gy = m-1;
queue<node>q;
q.push(node(0,0,""));
while(!q.empty()){
node p = q.front();
q.pop();
vis[p.x][p.y] = true;
if(p.x == gx && p.y == gy){
cout << p.z << endl;
return 0;
}
for(int i = 0;i < 4;i++){
int mx = p.x+dx[i],my = p.y+dy[i];
if(mx >= 0 && mx < n && my >= 0 && my < m && !vis[mx][my] && !maze[mx][my])
q.push(node(mx,my,p.z+ch[i]));
}
}
return 0;
}
總結:
怎麼現在回憶pdf上給的字典序排序是D<U<L<R?這不對呀。。(突然背後一絲涼意。。。)
第五題
題意:
RSA加密,給定p,q兩個質數,n=p*q,再給一個與(p-1)*(q-1)互質的d,就能找到一個e使得d*e%(p-1)(q-1) = 1
給定密文C,可根據C^e%n得出原文D
思路:
暴力i從2到sqrt(n),如果n%i==0,輸出i,n/i 很快就跑出來了p和q,驗算一下(p-1)(q-1)與d確實是互質的。設a = (p-1)(q-1)
則存在i∈[0,d),使(i*a+1)%d == 0,此時(i*a+1)/d即爲e,然後快速冪取模,pow(C,e,n)即可得出答案。
代碼:
求p,q,a部分:
bool isPrime(ll n){
ll x = sqrt(n);
for(ll i = 2;i <= x;i++)
if(n%i == 0)return false;
return true;
}
int main(){
ll n = xxx,d = yyy;
ll maxn = sqrt(n);
for(ll i = 2;i <= maxn;i++)
if(n % i == 0){
ll p = i,q = n/i,a = (p-1)*(q-1);
if(isPrime(p) && isPrime(q) && __gcd(a,d)==1)printf("%lld %lld %lld\n",p,q,a);
}
return 0;
}
求答案部分:
a = int(xxx)
n = int(yyy)
d = int(zzz)
C = int(???)
e = 0
for i in range(0,d):
if (i*a+1)%d == 0:
e = (i*a+1)/d
print(pow(C,int(e),n))
總結:
數論不好,如果沒有python恐怕要用__int128來做後半部分了。。還好這次給了python。。求出來的答案記得是18B,開頭字母爲8,正不正確還不知道。。。
第六題
題意:
給一個完全二叉樹,和每個點的值,問第幾層值之和最大。
思路:
完全二叉樹≠滿二叉樹。求一個完全二叉樹有幾層要看給的n(節點個數)的二進制最高位是第幾位。比如1的最高位爲第一位,只有一層;7(111)的最高位爲第三位,有三層,8(1000)的最高位爲第四位,有四層……對每一層求個和,和先前記錄的最大值比較一下,大了更新即可。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const ll INF = 0x3f3f3f3f3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
int main(){
redirect();
int n,x,depth = 0;
scanf("%d",&n);
x = n;
while(x)x>>=1,depth++;
ll maxval = -INF,ans;
for(int i = 1;i <= depth;i++){
ll sum = 0;
int cnt = 1<<(i-1);
while(cnt-- && ~scanf("%d",&x))sum += x;
if(sum > maxval)maxval = sum,ans = i;
}
printf("%I64d\n",ans);
return 0;
}
總結:
這題其實是有坑點的,首先不是滿二叉樹。。最後一層不一定是滿的。其次結點值可能是負的,那些取maxval = 0的涼了。還有這題好像爆int,要開long long。
第七題
題意:
有n個商家,初始熱度均爲0,他們在時間T分鐘內總共產生m個訂單。對一個商家,如果某分鐘沒接到訂單,熱度將-1(掉到0爲止不會再掉),每接到一個訂單,熱度+2,且這一分鐘熱度不會-1。對平臺來說,某個商家熱度到達6,將加入到一個名單上,當某商家熱度掉到3,將從名單上劃掉,問在時間T,處理完m個訂單之後,在名單上的商家個數。
思路:
對訂單集合按時間sort一下,每分鐘將每個商家熱度-1是不現實的,否則將會是O(n*T)複雜度,也就拿個暴力分了。
一個很自然的想法是,對於每個商家,開個last數組記錄上一次接到訂單是什麼時候,以便在下一次接單的時候能夠方便地計算出兩次接單的時間差,在此期間掉的熱度就知道了。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
struct node{
int t,x;
}p[maxn];
bool operator <(const node& a,const node& b){
return a.t < b.t;
}
int last[maxn];
int val[maxn];
bool live[maxn];
int main(){
redirect();
int n,m,T,cnt = 0;
scanf("%d %d %d",&n,&m,&T);
for(int i = 1;i <= m;i++)scanf("%d %d",&p[i].t,&p[i].x);
sort(p+1,p+1+m);
for(int i = 1;i <= m;i++){
int &t = p[i].t,&x = p[i].x;
val[x] = max(0,val[x]-max(0,t-last[x]-1)) + 2;
last[x] = t;
if(val[x] > 5)live[x] = true;
else if(val[x] <= 3)live[x] = false;
}
for(int i = 1;i <= n;i++){
val[i] -= T-last[i];
if(val[i] <= 3)live[i] = false;
if(live[i])cnt++;
}
printf("%d\n",cnt);
return 0;
}
總結:
同樣卡了很久。。而且不知道正確性。。。而且。。最關鍵的是。。這題叫“飽了麼”。。。都給沒吃早餐的我給看餓了。
第八題
題意:
給定一個數組a[],從頭到尾,對於每個數a[i],如果在i位置之前出現過,就一直+1,直到沒有在先前的數組裏出現過,這成爲新的a[i]
思路:
直接暴力O(n^2)顯然只能拿部分分。考慮到a[i]大小最大隻有1e6,可以開一個set,存入1~1e6(如果每個數都是1e6,而有1e5個,最大的數可到1.1e6-1),每次給一個數x,查詢set.lower_bound(x)代表的數,給這個位置就行了,然後再set裏把這個數刪掉。複雜度O(n*logm)(m爲集合裏數的數量)
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("out.txt","r",stdin);
freopen("ans.txt","a+",stdout);
#endif
}
set<int>s;
inline int read(){
int num = 0,w = 0;
char ch = 0;
while(!isdigit(ch)){
w |= ch == '-';
ch = getchar();
}
while (isdigit(ch)){
num = (num<<3) + (num<<1) + (ch^48);
ch = getchar();
}
return w? -num: num;
}
int main(){
redirect();
for(int i = 1;i < 1100000;i++)s.insert(i);
int n,x;
scanf("%d",&n);
for(int i = 1;i <= n;i++){
x = read();
set<int>::iterator it = s.lower_bound(x);
printf("%d%c",*it,i==n?'\n':' ');
s.erase(it);
}
return 0;
}
總結:
Process exited after 1.019 seconds with return value 0
這是上述代碼運行極端數據(1e5個1e6)的結果。時間1s有點兒緊。。能不能過還要看運氣。不過我這道題是過不了了。。想複雜腦抽寫了並查集,記錄一段連續區間的區間首,在區間首記錄區間長度……這思路根本就不對- -算法是fake的,都怪當時太緊張,測了下樣例過了,自己出了個n=10的隨機數據過了就給交了。。
另外有思路採用樹狀數組+二分,跟set在時間上應該差不了多少。
第九題
題意:
有n袋糖,裏面有m種糖,每袋糖有k個,問至少取幾包才能攢夠所有種類的糖。
n <= 100;m,k <= 20
思路:
莫名感覺很像區間覆蓋問題,假如一袋糖覆蓋一個區間的話。。。後來馬上否認了,一袋糖裏裝的糖的種類是隔開而不連續的。。
沒有太好的辦法,看到數據量決定還是dfs鋌而走險。記錄每袋糖擁有的糖果種類,同時記錄哪些袋種有某種糖果種類(相互映射關係),對糖果種類dfs暴搜+剪枝。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);
void redirect(){
#ifdef LOCAL
freopen("test.txt","r",stdin);
#endif
}
//s1爲某袋糖果裏有的糖果種類,s2爲某種類哪些袋子裏有
set<int>s1[110],s2[25];
int n,m,k,x;
bool a[110];//選哪些袋
int ans = INF;
void dfs(int x){
if(x > m){
int cnt = 0;
for(int i = 1;i <= n;i++)
if(a[i])cnt++;
ans = min(ans,cnt);
return;
}
for(int i = 1;i <= n;i++)
if(a[i] && s1[i].count(x))dfs(x+1);
for(set<int>::iterator it = s2[x].begin();it != s2[x].end();it++){
a[*it] = true;
dfs(x+1);
a[*it] = false;
}
}
int main(){
redirect();
scanf("%d %d %d",&n,&m,&k);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= k;j++){
scanf("%d",&x);
s1[i].insert(x);
s2[x].insert(i);
}
dfs(1);
printf("%d\n",ans==INF?-1:ans);
return 0;
}
總結:
太brute force了。。不一定能AC
第十題
題意:
給定T,k,其中T爲樣例數,k爲質數,對於每組樣例,給出n,m,求
思路:
只會根據C(i,j)=C(i-1,j) + C(i-1,j-1)遞推求n<=2000時的少組樣例,甚至連莫隊算法都沒法用。
代碼:
太brute force了,略了。
總結:
thu2016集訓原題,看來出題人要麼是thu老師,要麼是thu巨巨,要麼是有淵源的狼滅吧。
比賽總結
如果說去年不能進省一是有可能,而因爲運氣好進了,今年恐怕真就懸了,懸的是做題時狀態的飄忽不定,懸的是不認真讀題,各種腦抽,懸的是太過重視而壓力太大。實際上,去年賽前壓根沒想過會拿省一,就放下包袱輕裝上陣,結局正出人意料。
賽已完,只能夠祈福主辦方能給我一次北京的機會了。
2019-3-24 21:52於hhu