部分题解
前言
下午在群里面看到一个同学疯狂宣传这个比赛,处于好奇的态度(他说难度有cf1800)我参赛了,可能真的有cf1800的题目,只是我没做出来,离比赛还有十多分钟的时候到了吉首的OJ,注册了账号,稍稍等待之后进入了比赛,出于习惯开了A题,然后想了几分钟没有思路,然后切出去看榜单,看见有人过了J题,我赶紧开了J题,嗯,是个签到题,但是WA了一发,读题的时候漏掉了一个信息,改了后A了,然后开了C,立方和公式稍微一化简就能找到a和b和n的关系,然后一发A了,接着开了E,一发A,刷了下榜单,看到L过了十多人,第一发处理边界的时候出了问题,后面想了下可以直接O(N)扫过去找到最小的,第三发A了,然后此时我在20名左右(暂时靠前),于是又回去看A,稍微想了一下然后又写了个公式,然后自己测试都没过,于是我开始直接硬推公式,想了二十多分钟还是没想出来,于是我开始打表找找规律,在反复观察后,我发现了一个规律,但是当时睡意来了,看着面前的屏幕还有外面刺眼的光线,我的眼睛开始疼痛,强忍着,继续记录规律,在二十多分钟后我把规律转化为了公式,然后交上去第一发CE(本地是能跑的),我把头文件换成了万能头,交上去一发A了,从这一发A之后后面就做的太难受了,接着我开了D,很明显D是一个模拟题,但是一直写不对(写代码细节没写好),然后转身开了F,感觉是一个博弈论,第一发我直接交了cxy上去,然后过了一半的测试点,接着交了几法rand,没过就没看了,然后这个时候我发现K题有几十人切出来了,于是赶忙去切K,由于我的粗心,计算的时候写反了,贡献了5发罚时,此时已经是四点五十二了,最后转身去开D,在贡献了20发罚时的情况下,在最后的5分钟把D题A了,当时激动的喊了出来(^^)。
A: 学习快速幂
传送门
解题思路:通过打标找规律我们能发现,复杂度从\(2^{i}-1\)开始到\(2^{i}-1+2^{i-1}\)是连续的,然后通过两次求和可以算出公式
退出公式后我们就可以直接写代码啦^^
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007;
int cnt = 0;
ll qpow(ll a, ll b) {
if(b == 0) {
return 1%mod;
}
cnt++;
if(b%2 == 1) {
return (a*qpow(a,b-1))%mod;
}
else {
return (qpow(a,b/2)*qpow(a,b/2))%mod;
}
}
long long f(int n) {
long long ans = 0;
while(n) {
n/=2;
ans++;
}
return ans;
}
ll cal(ll n) {
ll ans = (ll)pow(2,n-1) * (ll) (pow(2,n)-1) + ((ll)pow(2,n-1)-1)*((ll)pow(2,n-1))/2;
return ans;
}
ll cal2(ll k, ll len) {
ll ans =len * (ll)(pow(2,k)-1) + (len-1LL)*len/2;
return ans;
}
int main()
{
int t;
// for(int i = 0;i < 5; ++i) {
// cnt = 0;
// qpow(1,i);
// printf("%d\n",cnt);
// }
scanf("%d",&t);
int n;
while(t--) {
scanf("%d",&n);
if(n == 0) {
puts("0");
continue;
}
long long k = f(n);
ll ans = 0;
for(ll i = 1;i <= k - 1; ++i) {
ans += cal(i);
}
ll len = max(0LL,n - (ll)(pow(2,k-1)-1));
ans += cal2(k,len);
printf("%lld\n",ans);
}
return 0;
}
C: LeeLdler的数字
传送门
解题思路:把立方和化简一下,我们就能得到a+b=n这个结论,所以随便交一个正整数数据满足之和尾n就行
Code:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
printf("%d %d\n",n-1,1);
return 0;
}
D: 珍爱生命,远离赌博
传送门
解题思路:我们可以开一个map作为标记,标记这张牌在之前的位置是否出现,然后开一个栈存放牌,输入一个字符串,我们遍历这个字符串,如果发现即将要放进去的这张牌在之前出现过,那么一定在栈中有相同的元素,我们直接对栈开始pop操作直到找到这个元素为止,在pop的过程中别忘了把这些元素的vis清除,如果将要放进去的字符串没有出现过那么久入栈,并且标记该元素已经出现过。注意的是,这题好像卡了常数,我的做法在复杂度上是\(O(N)\)的。
Code:
#define fastcall __attribute__((optimize("-O3")))
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")//这是优化常数的代码
#include<bits/stdc++.h>
using namespace std;
unordered_map<char,bool> vis;
unordered_map<char,int> mp;
char ch[2000006];
stack<char> S;
int main()
{
// string str;
// cin>>str;
scanf("%s",ch);
mp[\'A\'] = 1;mp[\'2\'] = 2;mp[\'3\'] = 3;mp[\'4\'] = 4;mp[\'5\'] = 5;mp[\'6\'] = 6;mp[\'7\'] = 7;mp[\'8\'] = 8;mp[\'9\'] = 9;mp[\'J\'] = 10;mp[\'Q\'] = 11;mp[\'K\'] = 12;
int l = 0, r = 0;
for(int i = 0,len = strlen(ch);i < len; ++i) {
if(vis[ch[i]]) {//S.size() &&
if(i & 1) {
while(S.size() && S.top() != ch[i]) {
vis[S.top()] = false;
r += mp[S.top()];
S.pop();
}
// r += 2 * mp[ch[i]];
if(S.size()) {
r += 2*mp[S.top()];
S.pop();
}
}
else {
while(S.size() && S.top() != ch[i]) {
vis[S.top()] = false;
l += mp[S.top()];
S.pop();
}
//l += 2 * mp[ch[i]];
if(S.size()) {
l += 2 * mp[S.top()];
S.pop();
}
}
vis[ch[i]] = false;
continue;
}
S.push(ch[i]);
vis[ch[i]] = true;
}
if(l == r) {
puts("-1");
}
else {
if(l < r) {
printf("lbg %d\n",r);
}
else
printf("zqc %d\n",l);
}
}
E: 会长的榜单
传送门
解题思路:这题就是把秒数转化为小时、分钟、秒数进行表示,如果该秒数为0则直接输出0
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 20;
int a[N];
int h[N];
int m[N];
int s[N];
int main()
{
char name[30];
int n;
cin>>n;
cin>>name;
for(int i = 0;i < n; ++i) {
cin>>a[i];
h[i] = a[i] / 3600;
a[i] -= h[i] * 3600;
m[i] = a[i] / 60;
a[i] -= m[i] * 60;
s[i] = a[i];
}
printf("%s ",name);
for(int i = 0;i < n; ++i) {
if(h[i] == 0 && m[i] == 0 && s[i] == 0)
printf("0%c",i ==n-1?\'\n\':\' \');
else
printf("%02d:%02d:%02d%c",h[i],m[i],s[i],i ==n-1?\'\n\':\' \');//格式化输出
}
}
J: 会长数
传送门
解题思路:暴力把数的每一位拿出来判断,并且把这些数的和判断是否是素数,其实满足条件的数只有23
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
bool is_prime(int n) {
if(n==0 || n== 1)
return false;
for(int i = 2;i *i <= n; ++i) {
if(n%i==0)
return false;
}
return true;
}
bool work(int i) {
int ans = 0;
while(i) {
if(!is_prime(i%10))
return false;
ans += i%10;
i/=10;
}
if(is_prime(ans))
return true;
return false;
}
int main()
{
vector<int> V;
for(int i = 10;i <= 100;++i) {
if(is_prime(i) && work(i)) {
V.push_back(i);
}
}
printf("%d\n",V.size());
for(int i = 0, len = V.size();i < len; ++i) {
printf("%d%c",V[i],i == len-1?\'\n\':\' \');
}
return 0;
}
K: 抽奖
传送门
解题思路:我们先统计商品的总数目,然后每次抽取会让商品的总数目减一,每次找到数目剩下的最多的商品,然后计算概率和当前抽取的商品的概率做差计算差值是否大于1/5,如果大于则表示不满足抽奖池的抽奖规律,做一个标记,最后判断标记是否更改,如果被更改的话,就输出BUG,否则输出PASS,时间复杂度\(O(N^2)\)
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int a[N];
int n;
double work() {
int max_ = a[1];
for(int i = 2;i <= n; ++i) {
max_ = max(a[i],max_);
}
return (double)max_;
}
int main()
{
int m,b;
scanf("%d",&n);
int tol = 0;
int max_key;
for(int i = 1 ;i <= n; ++i) scanf("%d",&a[i]),tol+=a[i];
scanf("%d",&m);
bool fg = true;
for(int i = 0;i < m; ++i) {
scanf("%d",&b);
if(!a[b]) {
fg = false;
continue;
}
double k = (double)work();
double k1 = 1.0*k/tol;
double k2 = 1.0*a[b]/tol;
if(abs(k1 - k2) > 0.2) {
fg = false;
}
tol --;
a[b] --;
}
if(fg)
puts("PASS");
else
puts("BUG");
return 0;
}
L: 馋嘴会长逛小吃街
传送门
解题思路:我们先对小吃摊的位置排序,然后从左往右每次去k个连续的小吃摊,然后扫过去找到最小的路程就OK了,时间复杂度\(O(N)\);
Code:
#include<bits/stdc++.h>
using namespace std;
int a[30];
int main()
{
int t;
int n,k;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&k);
for(int i = 1;i <= n; ++i) {
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
int ans = 0x3f3f3f3f;
for(int i = 1;i <= n - k + 1; ++i) {
ans = min(a[i + k - 1] - a[i], ans);
}
printf("%d\n",ans*2);
}
return 0;
}
总结
这次比赛前期状态还是可以,到了中后期思维、写代码速度还有写代码的仔细程度都有明显的下降,特别是D题,思路蛮简单,就是不仔细,写的代码的细节还是没处理好,J题(签到题)也WA了一发,得多加训练了。还有概率论和博弈论这俩东西得找个时间补补,感觉最近得比赛都在考这个,几乎每考必死。其他的题目,有时间我看看能不能补补==