2017.9.7 模拟考试

51 篇文章 0 订阅
45 篇文章 0 订阅

P76

这里写图片描述

【问题描述】
一张长度为�的纸带, 我们可以从左至右编号为0 − �(纸带最左端标号为
0)。 现在有�次操作, 每次将纸带沿着某个位置进行折叠, 问所有操作之后纸带
的长度是多少。
【输入格式】
第一行两个数字�, �如题意所述。
接下来一行�个整数代表每次折叠的位置。
【输出格式】
一行一个整数代表答案。
【样例输入】
5 2
3 5
【样例输出】
2
【样例解释】
树上有只鸟。
【数据规模与约定】
对于60%的数据, �, � ≤ 3000。
对于100%的数据, � ≤ 1018, � ≤ 3000。

去年在刷题班的时候就会,但是全忘了,现场推的。。。
还是写出来了,注意要开 unsigned long long
注意看N辣么大,M辣么小,我们肯定要从M上搞点事情。维护一个l,r表示当前的左右端点。要点有三

  • 当该折叠点在l-r区间的左伴部分的时候,之后的折叠点从左边向右边映射,l=该折叠点
  • 当该折叠点在l-r区间的右伴部分的时候,之后的折叠点从右边向左边映射,r=该折叠点
  • 注意开unsigned long long 才可以
#include<iostream>
#include<cstdio>
#define ULL unsigned long long
using namespace std;
ULL f[3005],n,L,R;
int m;
inline ULL read() {
    ULL w=0,flag=1;
    char ch=getchar();
    while(ch>'9'||ch<'0') {
        if(ch=='-')flag=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0') {
        w=w*10+ch-'0';
        ch=getchar();
    }
    return w*flag;
}
int main() {
    freopen("he.in","r",stdin);
    freopen("he.out","w",stdout);
    n=read();
    m=read();
    L=0;
    R=n;
    for(int i=1; i<=m; i++)f[i]=read();
    for(int i=1; i<=m; i++) {

        if(f[i]*2>=L+R) R=f[i];// 舍掉右边
        else L=f[i];//  舍掉左边

        for(int j=i+1; j<=m; j++) {
            if(f[j]>R)  f[j]=R*2-f[j];
            if(f[j]<L)  f[j]=L*2-f[j];
        }

    }

    cout<<R-L<<endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

这里写图片描述
这里写图片描述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e5+10;
int a[MAXN],top;

int erfen(int x){
    int l=1,r=top,Mid,ans=0;
    while(l<=r){
        Mid=l+r>>1;
        if(a[Mid]>=x) ans=Mid,r=Mid-1;
        else l=Mid+1;
    }
    return ans;
}

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); }  x*=f;
}

int main(){
    freopen("she.in","r",stdin);
    freopen("she.out","w",stdout);
    int i,j,k,n,m,s,l,r,x,ans,ans2,now,T;
    bool flag;
    read(T);
    while(T--){
        read(m),read(s),read(l),read(r);
        if(l>=m){ printf("-1\n"); continue; }
        if(r>=m) r=m-1;
        now=0,flag=0,top=0;
        for( n=1;n*n<=m;++n){
            now=(now+s)%m;
            if(l<=now&&now<=r){
                printf("%d\n",n);
                flag=1; break;
            }
            a[++top]=now;
        }
        --n;
        int sta=a[top];
        if(flag) continue;
        sort(a+1,a+top+1);
        for(now=1;now*n<=m;++now){
            l=(l-sta+m)%m;r=(r-sta+m)%m;
            if(l>r){
                if(a[top]>=l){ flag=1; break; }
                if(a[1]<=r)  { flag=1; break; }
            } else {
                if(a[top]<l) continue;
                int x=erfen(l);
                if(a[x]<=r) { flag=1; break; }
            }
        }
        if(!flag) { printf("-1\n"); continue; }
        ans=now*n,now=0;
        for(i=1;i<=top;++i){
            now=(now+s)%m;
            if(l<=now&&now<=r) break;
        }
        ans+=i;
        printf("%d\n",ans);
    }
    return 0;
}

看的题解,二分答案,不过前边的一顿操作猛如虎,没看懂…….

【问题描述】
�个人坐成一圈, 其中第�个人拿着一个球。 每次每个人会以一定的概率向
左边的人和右边的人传球。 当所有人都拿到过球之后, 最后一个拿到球的人即为
胜者。 求第�个人获胜的概率。(所有人按照编号逆时针坐成一圈)
【输入格式】
第一行一个数�代表数据组数。
对于每组数据, 第一行两个整数�, �如题意所述。
接下来每行一个实数�代表该人将球传给右边的人的概率。
【输出格式】
对于每组数据, 一行一个实数代表答案, 保留9位小数。
【样例输入】
1
5 1
0.10
0.20
0.30
0.40
0.50
【样例输出】
0.007692308
【样例解释】
然后鸟是我的。
【数据规模与约定】
对于20%的数据, � ≤ 3。
对于70%的数据, �, � ≤ 100。
对于100%的数据, � ≤ 10000,1 ≤ � ≤ 100。

概率DP,p表示向右传的概率,q表示向左传球的概率,可以看做是在合并两个相邻的人,合并到最后就好处理了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LDB long double
#define DB double
const int MAXN = 1000+10;
int T,n,k,pre[MAXN],next[MAXN];
LDB p[MAXN],q[MAXN];
void del(int b){
    int a=pre[b],c=next[b];
    LDB pa=p[a],pb=p[b],pc=p[c];
    p[a]=pa*pb/(1-pa*(1-pb)),q[a]=1-p[a];
    q[c]=(1-pc)*(1-pb)/(1-pb*(1-pc));
    p[c]=1-q[c];
    next[a]=c,pre[c]=a;
}

LDB solve(){
    if(n<=2) return 1;
    if(n<=3) return k==1 ? p[1] : q[2];
    for(int i=1;i<=n;++i) pre[i]=i-1,next[i]=i+1;
    pre[1]=n,next[n]=1;
    if(k==1){
        for(int i=2;i<n-1;++i) del(i);
        return p[1];
    }
    if(k==n-1){
        for(int i=2;i<n-1;++i) del(i);
        return q[n-1];
    }
    for(int i=2;i<n-1;++i)
        if(i!=k) del(i);
    del(k);
    return q[k]*p[1]+p[k]*q[n-1];
}

int main(){
    freopen("it.in","r",stdin);
    freopen("it.out","w",stdout);
    DB v;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;++i)
            scanf("%lf",&v),p[i]=v,q[i]=1-v;
        printf("%.9lf\n",(DB)solve());
    }
    fclose(stdin);fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值