Atcoder ABC 189 赛后解题报告
A - Slot
本题不难。我们可以随机选择一个字符作为基准,然后扫一遍字符串逐一比对就可以知道是否符合要求了。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n;
string s;
signed main() {
cin>>s;
n=s.size();
for(int i=0;i<n;i++) {
if(s[i]!=s[0]) {
cout<<"Lost
";
return 0;
}
}
cout<<"Won
";
return 0;
}
B - Alcoholic
本题不难,但是卡精度卡得难受(连 long double
都卡):
QWQ
解法很简单,只要把每次酒精的量算出来,加起来,只要发现在某一时刻大于了 (X),就输出,并终止程序。否则输出 (-1)。
既然他卡精度,我们只有一个解决办法就是避免小数的出现,我们只需要把 (X) 乘以 (100),就可以避免百分比的出现了。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=1e3+10;
const long double eps=1e-10 ;
int v[maxn],p[maxn];
int n;
int T,tot;
signed main() {
cin>>n>>T;
for(int i=1;i<=n;i++) {
cin>>v[i]>>p[i];
tot+=v[i]*p[i];
if(T*100<tot) {//这里处理一下
cout<<i<<endl;
return 0;
}
}
cout<<-1<<endl;
return 0;
}
C - Mandarin Orange
本题难度比前两题大,比赛时稍微思考了一会儿(5min),发现这个题很简单。。。
- 首先从时间复杂度入手。(nleq 10^4),说明要么就是 (O(n log^3 n)) 或者是 (O(n^2)) 带一点小优化。显然我们选择后者。
- 其次我们想,这个题肯定没那么复杂,一定是个朴素的解法,我们从问题本质想,其实就是找一个区间,找到其最小值,这个最小值乘以区间长度的最大值就是答案。我们反过来想,可以先确定最小值,找出其可以对应的最大区间即可。
很有精神!我们只要扫一遍,把每个数作为最小值都往两边扩展一下区间,擂台法算最大值即可。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e4+10;
int n,a[maxn],ans;
signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
for(int i=1;i<=n;i++) {
int l=i,r=i;
while(l-1>0&&a[l-1]>=a[i]) {
l--;
}
while(r+1<=n&&a[r+1]>=a[i]) {
r++;
}
ans=max(ans,(r-l+1)*a[i]);
}
cout<<ans<<endl;
return 0;
}
D - Logical Expression
说实话,这个题,(Nleq 60) 这个条件给得妙啊,成功让我在比赛时对我的线性算法怀疑了10min。。。。
与空气斗智斗勇
咳咳,闲言少叙,我们回归正题。我们尝试用 DP 来解决问题。
定状态
我们令 (f_{i,0/1}) 为进行完了前 (i) 次操作,结果为 (0/1) 的方案个数。
列转移方程
很好理解,如果理解不了建议补一补布尔运算。
定初始状态
我们的初始状态只有 (f_{0,0}) 和 (f_{0,1})。显然,他们都等于 (1)。
最后答案为 (f_{n,1})。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
const int maxn=61;
int n,f[maxn][2];
string s[maxn];
signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>s[i];
}
f[0][0]=f[0][1]=1;
for(int i=1;i<=n;i++) {
if(s[i][0]=='A') {
f[i][1]=f[i-1][1];
f[i][0]=f[i-1][1]+f[i-1][0]*2;
}
if(s[i][0]=='O') {
f[i][1]=f[i-1][1]*2+f[i-1][0];
f[i][0]=f[i-1][0];
}
}
cout<<f[n][1]<<endl;
return 0;
}
哪个大佬闲得蛋疼可以加强一下数据在评论区回复题目链接。
E - Rotate and Flip
本题有一个重要切入点,就是操作的顺序不会改变,这就提示,我们每一次变换完过后的变化是可以描述,且可叠加的。
- 先看旋转,设点 (A(x,y)),顺时针有:
那么 (A'(y,-x))。
- 同理逆时针旋转后 (A'(-y,x))。
- 沿直线 (x=p) 翻折得 (A'(2p-x,y))。
- 同理,沿直线 (y=p) 翻折得 (A'(x,2p-y))。
既然操作可以叠加,我们最后的坐标一定是可以在 (O(1)) 之内求解的,总时间复杂度 (O(n+m+q))。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=2e5+10;
int n,m,q,x[maxn],y[maxn];
struct op {
int rev,mulx,muly,addx,addy;
}p[maxn];
signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
x[i]=read();y[i]=read();
}
cin>>m;
p[0].mulx=p[0].muly=1;
for(int i=1;i<=m;i++) {
int t,v;
t=read();
if(t==1) {
p[i].rev=p[i-1].rev^1;
p[i].addx=p[i-1].addy;
p[i].addy=-p[i-1].addx;
p[i].mulx=p[i-1].muly;
p[i].muly=-p[i-1].mulx;
}
if(t==2) {
p[i].rev=p[i-1].rev^1;
p[i].addx=-p[i-1].addy;
p[i].addy=p[i-1].addx;
p[i].mulx=-p[i-1].muly;
p[i].muly=p[i-1].mulx;
}
if(t==3) {
p[i].rev=p[i-1].rev;
v=read();
p[i].addx=2*v-p[i-1].addx;
p[i].addy=p[i-1].addy;
p[i].mulx=-p[i-1].mulx;
p[i].muly=p[i-1].muly;
}
if(t==4) {
p[i].rev=p[i-1].rev;
v=read();
p[i].addx=p[i-1].addx;
p[i].addy=2*v-p[i-1].addy;
p[i].mulx=p[i-1].mulx;
p[i].muly=-p[i-1].muly;
}
}
cin>>q;
for(int i=1;i<=q;i++) {
int a=read(),b=read();
if(!p[a].rev)
printf("%lld %lld
",p[a].mulx*x[b]+p[a].addx,p[a].muly*y[b]+p[a].addy);
else {
printf("%lld %lld
",p[a].mulx*y[b]+p[a].addx,p[a].muly*x[b]+p[a].addy);
}
}
return 0;
}
码农题
F - Sugoroku2
这个题,我一眼,就看出来是个期望DP(废话)。
但就是在比赛时状态列得不好,挂掉了。。。。
总之我们还是一步一步来接这个问题。
定状态
我们设 (f_i) 为从 (i) 出发,到达 (n) 所需要的期望步数。
我在比赛设的是 (f_i) 表示由 (0) 出发到 (i) 的期望步数,这样不好处理退回到 (0) 的情况,因此我们采取上面一种。
列转移方程
也就是说如果我到了 (i) 要回到 (0),那么就要重新来过了。
很显然,这个转移方程不能直接计算,而是要解方程。我们很难直接入手。
我们可以这样想,我们最后求解的是 (f_0),而因为 (f_0) 的出现,导致不能直接计算,我们可以把 (f_0) 作为一个未知数,尝试用 (f_0) 来表示其他数,也就是说,(1leq ileq n) 时,(f_i=p_icdot f_0+q_i)。那么最后我们可以得到一个关于 (f_0) 的一元一次方程 (f_0=Pcdot f_0+Q)。((P,Q))都是有转移方程计算出来的。如果最后 (P=1),就说明无解,输出 (-1) 即可。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=1e5+10;
int n,m,k,b[maxn];
struct dp {
double p,q;
}f[maxn];
signed main() {
cin>>n>>m>>k;
for(int i=1;i<=k;i++) {
b[read()]=1;
}
double ps=0,qs=0;
int tot=1;
for(int i=n-1;i>=0;i--) {
if(i+m+1<=n) {
ps-=f[i+m+1].p;
qs-=f[i+m+1].q;
tot--;
}
if(!b[i]) {
f[i].p=ps/m;
f[i].q=qs/m+1.000;
}
else {
f[i].p=1.000;
f[i].q=0.000;
}
ps+=f[i].p;
qs+=f[i].q;
tot++;
}
if(fabs(1.00000-f[0].p)<=1e-12) {
printf("-1
");
}
else
cout<<fixed<<setprecision(6)<<f[0].q/(1-f[0].p)<<endl;
return 0;
}
另外还有一个二分的方法,这里就一笔带过略讲一下。
我们可以二分 (f_0),然后带回到原式中验证是否正确由于 (f_0)满足单调性,我们可以用二分答案来解决这个问题。
当然官方题解中还给了一个优化方法,就是把常用的 (mid=frac{l+r}{2}),改成 (mid=sqrt{lcdot r}),会更快一些。