CSP-J2022题解
/Limie
T1.乘方
简要题意:给定a,b,求a^b(a^b表示a的b次方)是否大于10^9,大于输出-1,小于等于输出a^b。
分析:此题直接枚举1~b会超时,故考虑用位数判断大小,a^b的的位数为c=[b*lg(a)+1](lg为以10为底的对数,[]为下取整)。c一旦大于10,则必定不成立,当c=10时,当前仅当a=10^9,b=1或a=10^3,b=3或a=10,b=9,除此以外,不成立,若c<10输出a^b即可(此步骤本人写快速幂)。
Code:
#include<bits/stdc++.h>
using namespace std;
long long a,b;
long long qmi(long long a,long long b)//快速幂
{
long long ans=1;
while(b){
if(b&1)ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
int main()
{
freopen("pow.in","r",stdin);
freopen("pow.out","w",stdout);
cin>>a>>b;
long long c=b*log10(a)+1;//取位数
if(a==10&&b==9||a==1000&&b==3||a==1000000000&&b==1){
cout<<1000000000;
return 0;
}
if(c>=10){
cout<<-1;
return 0;
}
cout<<qmi(a,b);
return 0;
}
T2.解密
简要题意:
给定n,e,d求一组正整数数对{p,q},满足p<=q且n=p*q且e*d=(p-1)*(q-1)+1
分析:
e*d=(p-1)*(q-1)+1=p*q-p-q+1+1=p*q-p-q+2=n-p-q+2,则p+q=n-e*d+2。
得到以下二元二次方程组:
p*q=n,p+q=n-e*d+2
令t=n-e*d+2
由韦达定理,一个一元二次方程(一般式为ax^2+bx+c=0,a不为0)的两根x1,x2满足x1+x2=-b/a,x1+x2=c/a(a,b,c同一般式中的a,b,c)
则将p,q看作两根,则c=na,b=-ta,用求根公式x=(-b±√(b^2-4ac))/(2*a),再次化简可得p,q=(t±√(t*t-4*n))/2,判断t*t-4*n非负,与t*t-4*n是否为完全平方数。
Code:
LL x=n-e*d+2,s=x*x-4*n;//LL表示long long
if(s<0){
printf("NO\n");
continue;
}
LL w=sqrt(s);
if(w*w!=s){
printf("NO\n");
continue;
}
if((x+w)%2!=0){
printf("NO\n");
continue;
}
if(x<=w){
printf("NO\n");
continue;
}
printf("%lld %lld\n",(x-w)/2,(x+w)/2);
T3.逻辑表达式
简要题意:
参考题面,题意很清楚。
分析:
大模拟,但是考虑情况较多,可用递归实现。主要思路为将整个表达式看作一个块,再根据括号将其分成更小的块进行处理,会发现处理每个块时操作一致,可以用递归实现,在判断短路时可判断‘&’和‘|’的个数,具体看代码。
(现在此代码在洛谷的Subtask 1中会TLE 5个点,但在其他OJ上跑的最多100ms)
Code(递归部分)
int dfs(int x,int y)//dfs返回表达式的值,ans1表示第一种短路的次数,ans2表示第二种短路的次数
{
int i,j,l=1;
for(i=x;i<=y;i++){
char ch=st[i];
if(ch=='('){
int c=1;
j=i+1;
while(j<=y){
if(st[j]=='(')c++;
if(st[j]==')')c--;//在第几层中
if(!c)break;
j++;
}
j--;
l&=dfs(i+1,j);
i=j+1;
}
else if(isdigit(ch))l&=(ch-'0');
else if(ch=='&'){
if(!l){
int c=0;
j=i+1;
ans1++;
while(j<=y){
if(!c&&st[j]=='|')break;
if(st[j]=='(')c++;
if(st[j]==')')c--;
if(!c&&st[j]=='&')ans1++;
j++;
}
i=j-1;
}
}
else{
if(l){
ans2++;
int c=0;
j=i+1;
while(j<=y){
if(st[j]=='(')c++;
if(st[j]==')')c--;
if(!c&&st[j]=='|')ans2++;
j++;
}
break;
}
l=1;
}
}
return l;
}
T4.上升点列
简要题意:
参照题面。
分析:
若不考虑“最多添加k个点”这个条件,则本题可化为最长不下降子序列问题,因为对于满足i<j且x[i]<=x[j]且y[i]<=y[j]的两点,一定可以通过加点将他们连在一起,最少加x[j]-x[i]+y[j]-y[i]-1个点
我们可以只考虑给定的点,而剩下的可添加在最后一列上(如图,在k上方也可添加点(5,11),(5,12)...)
则可列出状态转移方程:f[i]=max{f[j]}+1(i>j且x[i]>=x[j]且y[i]>=y[j])
再将“最多添加k个点”考虑进去,则不难设计状态f[i][j]表示表示选第i个点,前面最多添加j个点,则状态转移方程为:f[i][j]=max{f[l][u]}+1(i>l且x[i]>=x[l]且y[i]>=y[l]且0<=u<=j-(x[i]-x[l]+y[i]-y[l]-1),时间复杂度为O(n^2*k^2),而n=500,k=100,会超时。
不难发现对于给定的i,都有f[i][j]>=f[i][j-1],则状态转移方程为:f[i][j]=max{f[l][j-(x[i]-x[l]+y[i]-y[l]-1)]}+1,则时间复杂度降为O(n^2*k)。
提醒:点i要先按照x从小到大排序,x相同则按y从小到大排,用std::pair可轻松实现。
Code(DP部分)
for(i=1;i<=n;i++)
for(j=0;j<=m;j++){
for(k=1;k<i;k++)
if(a[i].x>=a[k].x&&a[i].y>=a[k].y&&j>=a[i].x-a[k].x+a[i].y-a[k].y-1)
f[i][j]=max(f[i][j],
f[k][j-(a[i].x-a[k].x+a[i].y-a[k].y-1)]);
f[i][j]++;//最后++,能避免初始化,代码量能减少
ans=max(ans,f[i][j]);
}
printf("%d",ans+m);//m为题目中的k
注:若有误,请指正。
标签:10,J2022,++,题解,long,st,int,&&,CSP From: https://www.cnblogs.com/Limie/p/16867030.html