zoukankan      html  css  js  c++  java
  • Atcoder ABC 189 赛后解题报告(A-F)

    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) 的方案个数。

    列转移方程

    [egin{cases} f_{i,0}=f_{i-1,1}+2cdot f_{i-1,0}&s_i=AND\ f_{i,1}=f_{i-1,1}&s_i=AND\ f_{i,0}=f_{i-1,0}&s_i=OR\ f_{i,1}=f_{i-1,0}+2cdot f_{i-1,1}&s_i=OR end{cases}]

    很好理解,如果理解不了建议补一补布尔运算。

    定初始状态

    我们的初始状态只有 (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

    本题有一个重要切入点,就是操作的顺序不会改变,这就提示,我们每一次变换完过后的变化是可以描述,且可叠加的。

    1. 先看旋转,设点 (A(x,y)),顺时针有:

    那么 (A'(y,-x))

    1. 同理逆时针旋转后 (A'(-y,x))
    2. 沿直线 (x=p) 翻折得 (A'(2p-x,y))
    3. 同理,沿直线 (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) 的情况,因此我们采取上面一种。

    列转移方程

    [egin{cases} f_i=frac{1}{m}cdotsumlimits_{k=1}^m f_{i+k}&forall x a_x eq i\ f_i=f_0&a_x=i end{cases}]

    也就是说如果我到了 (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}),会更快一些。

  • 相关阅读:
    LeetCode --- Climbing Stairs
    LeetCode --- Best Time to Buy and Sell Stock II
    LeedCode --- Best Time to Buy and Sell Stock
    git命令总结
    LeetCode --- Jump Game II
    Hdu 4497
    数据库lib7第4题创建存储过程
    Hdu 4496
    Hdu 4493
    快速排序
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/ABC189.html
Copyright © 2011-2022 走看看