【题解】CSP-J2022

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<=qn=p*qe*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/aa,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<jx[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]}+1i>jx[i]>=x[j]y[i]>=y[j]

再将“最多添加k个点”考虑进去,则不难设计状态f[i][j]表示表示选第i个点,前面最多添加j个点,则状态转移方程为:f[i][j]=max{f[l][u]}+1i>lx[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可轻松实现。

CodeDP部分)

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

 注:若有误,请指正。

posted @ 2022-11-07 18:50  Limie  阅读(423)  评论(0)    收藏  举报