bzoj上的一眼水题(上)

一眼水题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
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
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
#include
#include
#include
#include
#include
#include
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]q[i])break;
                seq[i]=seq[cnt];
            }
        }
    }
    return 0;
}

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

#include
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
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
#define INF 2e9
using 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-nowminprintf("%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 
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
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
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 
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
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.x;}
bool cmpr(rect A,rect B){
    if(A.a.x.a.x)return 1;
    if(A.a.x>B.a.x)return 0;
    return A.b.x.b.x;
}
bool cmpc(circ A,circ B){
    if(A.o.x-A.r.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.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.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.b.x
             &&p[que[j]].y>re[i].a.y
             &&p[que[j]].y.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.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).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
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
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;ifor(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
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
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*jelse 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;
}

4300.绝世好题
题解:与其说这是一道氘磷(DP)题,不如说是一道锑(Sb)题。。。

即使是这样苯蒟蒻一开始还是算错了。bi && b(i-1) != 0 不等价于整个b序列在某二进制位全为1。
就像这样:
01
11
10
cnt[j] 记录目前二进制第 j - 1位为1的最大长度;以 a 结尾的子序列最大长度是 max {cnt[j] + 1} (0 <= j <= 31 && a 的第 j 位 = 1)。

代码:

#include 
int cnt[35];
inline int getin () {
    int ret = 0, ng = 1; char c = getchar ();
    while (c < '0' || c > '9') {if (c == '-') ng = -1; c = getchar ();}
    while (c >= '0' && c <= '9') {ret = ret * 10 + c - '0'; c = getchar ();}
    return ret * ng;
}
int main () {
    //freopen (".in", "r", stdin);
    //freopen (".out", "w", stdout);
    int n, len = 0, now, a;
    n = getin ();
    for (int i = 1; i <= n; ++ i) {
        a = getin ();
        now = 0;
        for (int j = 0; j <= 30; ++ j)
            if (a & (1 << j) && now < cnt[j] + 1) now = cnt[j] + 1;
        for (int j = 0; j <= 30; ++ j)
            if (a & (1 << j)) cnt[j] = now;
    }
    for (int i = 0; i <= 30; ++ i)
        if (cnt[i] > len) len = cnt[i];
    printf ("%d\n", len);
    return 0;
}

你可能感兴趣的:(bzoj上的一眼水题(上))