前言
开一个新坑记录一下cf比赛,现在新号到了1550分(菜QAQ),个人实力基本就是div2做一半吧(菜的真实),希望之后早点把oi知识复习好了去打div1吧~。这场共5个题,赛中过了a c d,下面简单记录下做法吧。
A题
数每个字母出现数目能均分就输出YES。
B题
暴力枚举C值,c的幂超过一定限度直接跳出
由于指数函数爆炸增长使这个可以过(具体复杂度不太会)
比赛中我觉得我过了,结果只是当时的数据点过了,最后wa51 TAT
错因是我原来写的(if(ans==0) ans=now;else ans=min(ans,now);)
一般没啥问题,但那个点ans正好是0,结果就被之后的数字代替掉了...Orz
Code
//By zuiyumeng
#pragma GCC optimize(2)
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Re register
#define Ms(a,b) memset((a),(b),sizeof(a))
#define Fo(i,a,b) for(Re LL i=(a),_=(b);i<=_;i++)
#define Ro(i,a,b) for(Re LL i=(b),_=(a);i>=_;i--)
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
char buf[1<<21],*p1,*p2;
inline int read() {
int x=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+10;
LL n,mx;
LL a[N];
int main() {
n=read();
Fo(i,1,n) a[i]=read();
sort(a+1,a+1+n);
LL ans=-1,now=0;
Fo(i,1,a[n]) {
now=0;
LL tmp=1,flag=0;
Fo(j,1,n) {
now+=abs(a[j]-tmp);
tmp*=i;
if(tmp>(LL)1e14) {flag=1;break;}
}
if(flag) break;
if(ans==-1) ans=now; else ans=min(ans,now);
}
cout<<ans<<endl;
return 0;
}
C题
有点思维含量的题,就是构造一种特殊方式满足题意
第一步将第一个数变成0
第二步让第一个到最后一个数减去(a[i]*n) (此时a[1]为0)
第三步让第二个到最后一个数加上(a[i]*(n-1))
另外注意n=1特判,具体看代码:
Code
//By zuiyumeng
#pragma GCC optimize(2)
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Re register
#define Ms(a,b) memset((a),(b),sizeof(a))
#define Fo(i,a,b) for(Re LL i=(a),_=(b);i<=_;i++)
#define Ro(i,a,b) for(Re LL i=(b),_=(a);i>=_;i--)
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
char buf[1<<21],*p1,*p2;
inline int read() {
int x=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+10;
LL n;
LL a[N];
int main() {
n=read();
Fo(i,1,n) a[i]=read();
cout<<1<<" "<<1<<endl;
cout<<-a[1]<<endl;
if(n==1) {
cout<<1<<" "<<1<<endl;
cout<<0<<endl;
cout<<1<<" "<<1<<endl;
cout<<0<<endl;
} else {
cout<<1<<" "<<n<<endl; a[1]=0;
Fo(i,1,n) cout<<-a[i]*n<<" ";cout<<endl;
cout<<2<<" "<<n<<endl;
Fo(i,2,n) cout<<a[i]*(n-1)<<" ";cout<<endl;
}
return 0;
}
D题
似乎这个题用贪心(最优策略)硬模拟就能过(就是每次T找最大的HL找第二大的各减一然后排序重复直到出现0= =)
稍微说一下这个贪心原理:首先我肯定尽量避免选1,因为这样会导致我下一回合(这里将两人各拿一次为一回合),有可能没有地方选(因为根据游戏规则一个人可以一直拿一堆并且别人不能抢)。然后越大的数会让不得不换堆的时刻到来的越晚。贪心正确性(或者说最优性)在于先手按照贪心策略可以保证自己拿的永远是最大值即保证换堆时刻>=后手,后手按照贪心策略可以保证换堆时刻尽量延后并接近先手(最终换堆时刻相同则后手胜利)。
我是通过在这个基础上手玩小数据找规律过的2333
具体来说就是找最大值,然后用其他数的和减去最大值再加+1,用这个数和0取max,得到的数奇数为HL偶数为T
比赛时我只知道这个是对的但没想明白,赛后尝试解释一下:
- 当其他数的和减去最大值为负时,T只需要死咬住最大值不放就可胜利
- 当其他树的和减去最大值为正时,考虑原来的那种贪心策略,可以发现T永远能拿到当前情况下的最大值,那么游戏进行到最后两数时如果原来总数为奇数那么必为1 0,T先挑获胜,如果总数为偶数那么必为1 1,T先挑输掉。这里的话由于两数之差和两数之和奇偶性相同并且又+1,所以是上面的结论。
分析到这就发现这游戏其实就看最大数能不能吃到底,不能的话看总数是奇数还是偶数= =
//By zuiyumeng
#pragma GCC optimize(2)
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Re register
#define Ms(a,b) memset((a),(b),sizeof(a))
#define Fo(i,a,b) for(Re int i=(a),_=(b);i<=_;i++)
#define Ro(i,a,b) for(Re int i=(b),_=(a);i>=_;i--)
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
char buf[1<<21],*p1,*p2;
inline int read() {
int x=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+10;
int n,t,sum,mx;
int a[N];
int main() {
t=read();
while(t--) {
n=read(); mx=0; sum=0;
Fo(i,1,n) a[i]=read(),sum+=a[i];
Fo(i,1,n) if(!mx) mx=a[i]; else mx=max(mx,a[i]);
sum-=mx*2;
sum=max(sum+1,0);
if(sum%2) cout<<"HL"<<endl;
else cout<<"T"<<endl;
}
return 0;
}
E题
还没做(没看明白0.0),留坑反正下次cf还有好几天qwq
2020.9.2更新:
做完感觉算一个比较简单的dp(()虽然调了4个小时zz错误TAT()),但对于我这个很久不打dp的cj来说还是有点费劲= =。
显然每到一个等级我们可以选择1.直接清完 2.boss留1血后转移,然后我们发现当需要继续往前走的时候直接选择前进一步退回来清掉boss是最优情况,但由于我们最终可以不在n处,所以当我们走到最后时可以选择一直往回走清掉路上的boss。
于是我们发现路径上的抉择可以分为前后两部分,前一部分都是继续往前走,后一部分要等最后返回来清掉boss。
这里设dp[i]表示按照前一部分的规则走到i处的最小时间,得到dp[]的值后我们可以从后往前枚举中间的分界点,再累加后面情况的和并与dp相加更新答案。
需要注意的点:
- 当有两个前一部分的等级都选择boss留1血时,可以选择共用一个来回来实现两个都走两次。
- 写转移时候数清楚走了几个d (()数错可能也可以过样例Orz())
Code
//By zuiyumeng
#pragma GCC optimize(2)
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Re register
#define Ms(a,b) memset((a),(b),sizeof(a))
#define Fo(i,a,b) for(Re LL i=(a),_=(b);i<=_;i++)
#define Ro(i,a,b) for(Re LL i=(b),_=(a);i>=_;i--)
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
char buf[1<<21],*p1,*p2;
inline int read() {
int x=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e6+10;
LL n,ans,d;
LL a[N],r[4],dp[N];
LL val1(int x) {return a[x]*r[1]+r[3];}
LL val2(int x) {return min(r[1]*(a[x]+2ll),r[2]+r[1]);}
int main() {
n=read(); Fo(i,1,3) r[i]=read(); d=read();
Fo(i,1,n) a[i]=read();
if(n==1) {cout<<min(val1(1),val2(1));return 0;}
dp[0]=-d; dp[1]=val1(1);
Fo(i,1,n) {
dp[i]=min(dp[i],d+dp[i-1]+min(val1(i),val2(i)+2ll*d));
if(i<n) dp[i+1]=dp[i-1]+val2(i)+val2(i+1)+4ll*d;
}
LL sum=min(val1(n),val2(n)+2ll*d); ans=dp[n];
Ro(i,1,n-1) {
sum+=min(val1(i),val2(i));
ans=min(ans,dp[i-1]+sum+2ll*d*(n-i)+d);
}
cout<<ans;
return 0;
}