bzoj上的一眼水题(上)

来源:互联网 发布:javascript var obj 编辑:程序博客网 时间:2024/06/07 20:29

一眼水题orz

(截至2017-2-16前在bzoj上做的一眼水题)
bzoj上的题目链接形式:
http://www.lydsy.com/JudgeOnline/problem.php?id=题号
例如: 题号1000链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1000
(之后每道题就不粘链接了)


1000.A+B Problem
题解:a+b
代码:

#include<cstdio>using namespace std;int main(){    int a,b;    scanf("%d%d",&a,&b);    printf("%d\n",a+b);    return 0;}

1008.越狱
题解:用总方案数减去不会越狱的方案数.
总方案数:m^n
不合法方案数:第一个房间m种选择,后面n-1个犯人每人有m-1种选择,即 m*(m-1)^(n-1)
快速幂计算,两者相减即为答案。
代码:

#include<bits/stdc++.h>const int MOD=100003;long long qpow(long long x,long long n){    long long ret=1;    while(n)    {        if(n&1)            ret=ret*x%MOD;        x=x*x%MOD;        n>>=1;    }    return ret;}using namespace std;int main(){    long long m,n;    scanf("%lld%lld",&m,&n);    long long ans=(qpow(m,n)-qpow(m-1,n-1)%MOD*m%MOD+MOD)%MOD;    printf("%lld\n",ans);    return 0;}

1012.最大数maxnumber
题解:线段树。我不会告诉你这道题可以队列暴力
代码:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<cstdlib>#include<ctime>using namespace std;int seq[200005];char c[5];int main(){    int m,d,t=0,cnt=0,l,n;    scanf("%d%d",&m,&d);    while(m--)    {        scanf("%s",c);        if(c[0]=='Q')        {            scanf("%d",&l);            t=seq[cnt+1-l];            printf("%d\n",t);        }        if(c[0]=='A')        {            scanf("%d",&n);            cnt++;seq[cnt]=(n+t)%d;            for(int i=cnt-1;i>0;i--)            {                if(seq[cnt]<seq[i])break;                seq[i]=seq[cnt];            }        }    }    return 0;}

1022.小约翰的游戏John
题解:Nim游戏。。。SG函数策略证明什么的网上有,算个异或和(全为1特判)
代码:

#include<bits/stdc++.h>using namespace std;int main(){    int t,n,a,sg;    bool pd;    scanf("%d",&t);    while(t--){        scanf("%d",&n);        sg=0;pd=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&a);            sg^=a;            if(a!=1)pd=1;        }        if((sg&&pd)||(!sg&&!pd))            puts("John");        else puts("Brother");    }    return 0;}

1034.泡泡堂BNB
题解:贪心,得分最多是排序后从蔃到蒻排名相同的比赛,如果比不过就用最蒻的来比赛,得分最少的就是对方得分最多的情况用总分减去即可。
代码:

#include<bits/stdc++.h>using namespace std;int work(int a[],int b[],int n){    int la=1,lb=1,ra=n,rb=n,ans=0;    while(la<=ra&&lb<=rb)    {        if(a[la]>b[lb])++la,++lb,ans+=2;        else if(a[ra]>b[rb])--ra,--rb,ans+=2;        else{            if(a[la]==b[rb])ans++;            ++la,--rb;        }    }    return ans;}int a[100005],b[100005];int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;++i)scanf("%d",&a[i]);    for(int i=1;i<=n;++i)scanf("%d",&b[i]);    sort(a+1,a+1+n);sort(b+1,b+1+n);    printf("%d %d\n",work(a,b,n),2*n-work(b,a,n));    return 0;}

1047.理想的正方形
题解:滑动窗口+单调队列。
然而苯蒟蒻:暴力
代码:

#include<bits/stdc++.h>#define INF 2e9using namespace std;int mat[1005][1005],minn[1005][1005],maxn[1005][1005];int main(){    //freopen(".in","r",stdin);    //freopen(".out","w",stdout);    int a,b,n,ans=INF,nowmax,nowmin;    scanf("%d%d%d",&a,&b,&n);    for(int i=1;i<=a;++i)        for(int j=1;j<=b;++j)            scanf("%d",&mat[i][j]);    memset(minn,0x7f,sizeof(minn));    for(int i=1;i<=a;++i)        for(int j=1;j<=b-n+1;++j)            for(int k=j;k<=j+n-1;++k)            {                maxn[i][j]=max(maxn[i][j],mat[i][k]);                minn[i][j]=min(minn[i][j],mat[i][k]);            }    for(int i=1;i<=a-n+1;++i)        for(int j=1;j<=b-n+1;++j)        {            nowmax=0,nowmin=INF;            for(int k=i;k<=i+n-1;++k)            {                nowmax=max(nowmax,maxn[k][j]);                nowmin=min(nowmin,minn[k][j]);                if(nowmax-nowmin>=ans)break;            }            if(nowmax-nowmin<ans)ans=nowmax-nowmin;        }    printf("%d\n",ans);    return 0;}

1087.互不侵犯King
题解:基础状压dp,记一下每行的状态,状压01表示放没放转成二进制数,cnt表示一行的棋子个数,预处理出一行相邻格子不能放,即不可行的状态lgl(可行)和upn(相邻),每行状态只跟上一行有关,dp即可。
初始化:dp[1][cnt[i]][i] = 1 (0<=i<2^n, lgl[i])
方程式:dp[i][p+cnt[j]][j] = dp[i][p][l] (2<=i<=n, 0<=j,l<2^n, cnt[l]<=p<=k-cnt[j], lgl[j]&&lgl[l]&&upn[j][l])
代码:

#include <bits/stdc++.h>using namespace std;int cnt[515];bool lgl[515], upn[515][515];long long dp[11][85][515];int main (){    //freopen (".in", "r", stdin);    //freopen (".out", "w", stdout);    int n, k;    long long ans = 0;    scanf ("%d%d", &n, &k);    int nst = 1 << n;    for (int i = 0; i < nst; ++ i)        if (!(i & (i >> 1))) {            lgl[i] = 1;            int sum = 0;            for (int j = i; j; j >>= 1)                sum += j & 1;                cnt[i] = sum;        }    for (int i = 0; i < nst; ++ i)        if (lgl[i]) for (int j = 0; j < nst; ++ j)            if (lgl[j] && !(i & j) && !(i & (j >> 1)) && !((i >> 1) & j))                upn[i][j] = 1;    for (int i = 0; i < nst; ++ i)        if (lgl[i]) dp[1][cnt[i]][i] = 1;    for (int i = 2; i <= n; ++ i)        for (int j = 0; j < nst; ++ j)            if(lgl[j]) for (int l = 0; l < nst; ++ l)                if (lgl[l] && upn[j][l])for (int p = cnt[l]; p <= k - cnt[j]; ++ p)                    dp[i][p + cnt[j]][j] += dp[i - 1][p][l];    for (int i = 0; i < nst; ++ i)        ans += dp[n][k][i];    printf ("%lld\n", ans);    return 0;}

1088.扫雷Mine
题解:递推思想,a数组记录第二行格子相邻炸弹数,f数组表示要求的第一行格子相邻炸弹数,可以递推得f[i+1]=a[i]-f[i]-f[i-1],分类讨论a[1], f[1], f[2]的情况,统计方案数。
代码:

#include<bits/stdc++.h>using namespace std;int n,ans;int a[10001],f[10001];bool jud(){       for(int i=2;i<=n;i++)       {          f[i+1]=a[i]-f[i]-f[i-1];          if(f[i+1]<0)return 0;        }     if(a[n]-f[n-1]-f[n]!=0)return 0;       return 1;}int main(){       scanf("%d",&n);       for(int i=1;i<=n;i++)scanf("%d",&a[i]);       if(a[1]==0)ans+=jud();       else if(a[1]==1)       {                f[1]=1;ans+=jud();                memset(f,0,sizeof(f));                f[2]=1;ans+=jud();       }       else {f[1]=f[2]=1;ans+=jud();}       printf("%d",ans);       return 0;}

1192.鬼谷子的钱袋
题解:表示成二进制数是最少的,就是求m的二进制位数。
代码:

#include<bits/stdc++.h>using namespace std;int main(){    int m,ans=0;    scanf("%d",&m);    while(m)    {        m>>=1;        ans++;    }    printf("%d\n",ans);    return 0;}

1193.马步距离
题解:贪心+暴搜
   距离终点比较远的时候贪心:设终点与现在位置的横坐标差为xs,纵坐标差为ys,下一步位移的横纵坐标正负即xs。ys的符号;如果|xs|>|ys|,横坐标位移绝对值为2,纵坐标绝对值为1,否则反之。
   移动到以终点为重心5*5范围内时,贪心会出现错误。此时采用暴搜即可。
代码:

#include <bits/stdc++.h>using namespace std;const int f[7][7] = {    {0, 3, 2, 3, 2},    {3, 2, 1, 2, 3},    {2, 1, 4, 3, 2},    {3, 2, 3, 2, 3},    {2, 3, 2, 3, 4}};int main (){    //freopen (".in", "r", stdin);    //freopen (".out", "w", stdout);    int xp, yp, xs, ys, x, y, ans = 0;    scanf ("%d%d%d%d", &xp, &yp, &xs, &ys);    x = abs (xp - xs); y = abs (yp - ys);    while (x > 4 || y > 4) {        if (x < y) x -= 1, y -= 2;        else x -= 2, y -= 1;        x = abs (x); y = abs (y);        ++ ans;    }    ans += f[x][y];    printf ("%d\n", ans);    return 0;}

1199.汤姆的游戏
题解:标算应该是对于每个点二分一下判断,或者线段树一类。
   这道题可以用队列:先把圆当成它的边平行于坐标轴的外接正方形,转换成矩形。再使用队列维护一下在每个矩形x范围内的点,暴力判断y是否在范围内;如果是圆还要判断一下到圆心距离和半径的关系。
代码:

#include<bits/stdc++.h>using namespace std;struct point{double x,y;int bh;}p[10005];struct rect{point a,b;}re[250005];struct circ{point o;double r;}ci[250005];int ans[10005],que[10005];char c[2];double pf(double q){return q*q;}bool cmp(point A,point B){return A.x<B.x;}bool cmpr(rect A,rect B){    if(A.a.x<B.a.x)return 1;    if(A.a.x>B.a.x)return 0;    return A.b.x<B.b.x;}bool cmpc(circ A,circ B){    if(A.o.x-A.r<B.o.x-B.r)return 1;    if(A.o.x-A.r>B.o.x-B.r)return 0;    return A.o.x+A.r<B.o.x+B.r;}int main(){    int n,m,nr=0,nc=0,now,head,tail;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i)    {        scanf("%s",c);        if(c[0]=='r'){++nr;scanf("%lf%lf%lf%lf",&re[nr].a.x,&re[nr].a.y,&re[nr].b.x,&re[nr].b.y);}        if(c[0]=='c'){++nc;scanf("%lf%lf%lf",&ci[nc].o.x,&ci[nc].o.y,&ci[nc].r);}    }    for(int i=1;i<=m;++i)        {scanf("%lf%lf",&p[i].x,&p[i].y);p[i].bh=i;}    sort(p+1,p+m+1,cmp);    sort(re+1,re+nr+1,cmpr);    sort(ci+1,ci+nc+1,cmpc);    now=1,head=0,tail=-1;    for(int i=1;i<=nr;++i)    {        while(p[now].x<=re[i].a.x&&now<=m)++now;        while(p[que[head]].x<=re[i].a.x&&head<=tail)++head;        while(p[now].x<re[i].b.x&&now<=m){++tail;que[tail]=now;++now;}        for(int j=head;j<=tail;++j)        {            if(p[que[j]].x>re[i].a.x             &&p[que[j]].x<re[i].b.x             &&p[que[j]].y>re[i].a.y             &&p[que[j]].y<re[i].b.y)                ++ans[p[que[j]].bh];        }    }    now=1,head=0,tail=-1;    for(int i=1;i<=nc;++i)    {        while(p[now].x<=ci[i].o.x-ci[i].r&&now<=m)++now;        while(p[que[head]].x<=ci[i].o.x-ci[i].r&&head<=tail)++head;        while(p[now].x<ci[i].o.x+ci[i].r&&now<=m){++tail;que[tail]=now;++now;}        for(int j=head;j<=tail;++j)        {            if(pf(p[que[j]].x-ci[i].o.x)+pf(p[que[j]].y-ci[i].o.y)<pf(ci[i].r))                ++ans[p[que[j]].bh];        }    }    for(int i=1;i<=m;++i)        printf("%d\n",ans[i]);    return 0;}

1432.Function
题解:这个。。。苯蒟蒻也不是太会,找规律。发现如果这些函数以一种方式排列,第k层有2k段,当然可以将第k层转化为n-k+1层,两种方法取min,注意n==1时特判。
代码:

#include<bits/stdc++.h>using namespace std;int main(){    int n,k;    scanf("%d%d",&n,&k);     if(n==1)puts("1");    else printf("%d\n",2*min(k,n-k+1));    return 0;}

1800.fly 飞行棋
题解:n<=20???这个数据范围简直是暴殄天物!
O(n^4)做法:暴力枚举哪4个点,判断顺次两点之间的圆弧,相对的是否相等。
O(n^2)做法:预处理圆弧长度前缀和,枚举两个点看之间的圆弧是否为周长的一半,统计直径个数,每两个不同的直径对应着一个矩形。
O(n)做法:计算直径个数时无需枚举,按顺时针顺序处理即可。
代码 O(n^2):

#include<bits/stdc++.h>using namespace std;int sum[25];int main(){    int n,d=0,a;    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%d",&a);        sum[i]=sum[i-1]+a;    }    for(int i=1;i<n;i++)        for(int j=i+1;j<=n;j++)            if(sum[j]-sum[i]==sum[n]>>1)                d++;    printf("%d\n",d*(d-1)/2);       return 0;}

1968.COMMON 约数研究
题解:稍微逆向思维一下,对于每一个1<=i<=n,在<=n的范围内是n/i个数的约数,直接统计对答案的贡献即可。答案为:ni=1ni
代码:

#include<bits/stdc++.h>using namespace std;int main(){    int n,ans=0;    scanf("%d",&n);    for(int i=1;i<=n;i++)        ans+=n/i;    printf("%d\n",ans);    return 0;}

2257.瓶子和燃料
题解:感性认识一下,操作能的得到的最小正体积是这些瓶子容量的gcd(好像有一个叫裴蜀定理的东西,表示并不会),把这n个数的所有约数放在一起排序,找到最大的出现次数大于k的就约数即为答案。
代码:

#include<bits/stdc++.h>using namespace std;int Div[2000005];int main(){    int n,num=0,cnt=0,v,k;    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)    {        scanf("%d",&v);        for(int j=1;j*j<=v;j++)            if(v%j==0)            {                if(j*j<v){Div[++num]=j;Div[++num]=v/j;}                else Div[++num]=j;            }    }    sort(Div+1,Div+num+1);    for(int i=num;i>=1;i--)    {        if(Div[i]!=Div[i+1])cnt=1;        else cnt++;        if(cnt>=k){printf("%d\n",Div[i]);break;}    }    return 0;}
0 0
原创粉丝点击