zoukankan      html  css  js  c++  java
  • 机房测试模拟2:模拟+数学+数位dp

    T1:

     分析:

    画图模拟,发现折叠后的长度会变成折叠位置左右两部分取max,难点在于:折叠后找不到对应的点在哪个位置。

    因为n很大,但其实很多位置都是用不到的,所以处理m个操作。

    一次操作后,可能会影响到后面的所有操作的位置,于是将操作离线,一次操作后将后面所有将要用到的操作位置都更新。

    怎么更新?

    例如:1 2 3 4 5 6 7 8

    现在折3,下一步折8,8应该变成abs(3-8)=5

    因为相当于将节点重新标号了:

    1 2 3 4 5 6 7 8

    /  /  0 1 2 3 4 5(被翻过的位置不会再有了)

    然后长度又被更新成:len=max(len-a[i],a[i])

    (相比起来这种神奇又巧妙的方法ssw的模拟好理解多了)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define ri register int
    #define N 3005
    ll a[N];
    int main()
    {
        freopen("fold.in","r",stdin);
        freopen("fold.out","w",stdout);
        ll n; int m;
        scanf("%lld%d",&n,&m);
        ll len=n;
        for(ri i=1;i<=m;++i) scanf("%lld",&a[i]); 
        for(ri i=1;i<=m;++i){
            if(len==1) break;
            for(ri j=i+1;j<=m;++j) a[j]=abs(a[j]-a[i]);//相当于是把左边的翻转过来重新标号了 
            len=max(len-a[i],a[i]);//被划分成两段 长的那一段是答案 
        }
        printf("%lld
    ",len);
    }
    /*
    5 2
    3 5
    
    10 3
    3 5 2 
    */
    View Code

    T2:

    M S L R<=1e9

    分析:

    先写出式子:L <= (S*x) mod M <= R

    移项化简: (-r mod s ) <= M*y mod s <= ( -L mod s )(不知道为什么同%s可以成立,感性理解吧)

    将这个问题转换成了子问题,递归求解即可。

    细节一堆。。。

    先找到可行的简单解作为边界:

    若L<=S*x<=R,则x是一个可行解。

    若S%M==0,且L不为0,则无解。

    详见代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll dfs(ll m,ll s,ll l,ll r)
    {
        if(l==0) return 0;
        if(l>r || s%m==0 || l>=m) return -1;//如果s%m==0而l又不为0 显然无解 
        s%=m;//没有会超时!! 因为s再大也没有用 最后的式子都是要mod m的 
        ll tmp=(l-1)/s+1;//ceil 相当于上取整 
        if(tmp*s>=l && tmp*s<=r) return tmp;//找到第一个是s的倍数 且在范围内的数 
        ll mm=s,ss=m,lr=(-r%s+s)%s,rr=(-l%s+s)%s;//递归求解 
        tmp=dfs(mm,ss,lr,rr);
        if(tmp==-1) return -1;
        ll x=(m*tmp+r)/s;//取一边的r算出一个x,再看这个x是否大于等于l 
        if(x*s-m*tmp>=l) return (x%m+m)%m;//%m+m%m 保证x为最小正整数 
        return -1;
    }
    int main()
    {
        freopen("solve.in","r",stdin);
        freopen("solve.out","w",stdout);
        int T;
        scanf("%d",&T);
        while(T--){
            ll m,s,l,r;
            scanf("%lld%lld%lld%lld",&m,&s,&l,&r);
            if(m<=l) { printf("-1
    "); continue; }
            printf("%lld
    ",dfs(m,s,l,min(m-1,r)));//注意上界要和m-1取min 
        }
    }
    /*
    1
    5 4 2 3
    */
    View Code

    T3:

     分析:

    一眼数位dp,但是dp状态的设置很重要。

    设dp[i][j][s1][s2]为:现在在第i位,当前前缀的长度为j,当前前缀翻转后与l,r分别比较的大小。

    如何求当前翻转后的大小关系?

    若这一位大于l对应的一位,则是大于,等于则由上一位的大小关系决定。(r同理)

    但要考虑到有前导0的情况,所以第一位要强制从1开始,然后枚举第一位从哪个开始即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define usll unsigned long long
    #define ri register int
    #define N 25
    usll dp[N][N][3][3];
    int num[N],A[N],B[N],C[N],cnt=0,ltot=0,rtot=0;
    void init(usll x,int *num,int &xx)
    {
        xx=0;
        while(x) { num[++xx]=x%10; x/=10; }
    }
    int cmp(int x,int y,int last)
    {
        if(x<y) return 0;//<
        if(x==y) return last;
        return 2;
    }
    usll dfs(int x,int y,int s1,int s2,int lim)//x是现在到第x位 y是已经填了y位 s1与s2是与l和r相比较的值 
    {
        if(dp[x][y][s1][s2]!=-1 && !lim) return dp[x][y][s1][s2];
        if(x==1){
            if(y<ltot) s1=0; if(y<rtot) s2=0;//位数不够的话 肯定是小于l或r的 
            if(s1!=0 && s2!=2) return 1;//当这个数倒过来大于等于l且小于等于r即可 
            return 0; 
        }
        int up=lim ? C[x-1] : 9;//x-1:是因为这里的x是已经被填的 要枚举的是下一次要填的 对应x-1位 
        usll ret=0;
        for(ri i=0;i<=up;++i){
            ret+=dfs(x-1,y+1,cmp(i,A[y+1],s1),cmp(i,B[y+1],s2),lim && i==up);
        }
        if(!lim) dp[x][y][s1][s2]=ret;
        return ret;
    }
    usll solve()
    {
        usll ret=0;
        memset(dp,-1,sizeof(dp));
        for(ri i=1;i<=cnt;++i){
            int up=(i==cnt) ? C[cnt] : 9;//cnt->highest
            for(ri j=1;j<=up;++j)
            //枚举i 指前cnt-i位填0 后i位有值 比较的是A[1]因为翻转后 这一位相当于l的最后一位 而最后一位对应的下标是1
            //last定义为1 是因为如果这一位与l相同 则当作相同 不会出现l的位数大于填的数的情况 因为在dfs里面已经考虑了 
            ret+=dfs(i,1,cmp(j,A[1],1),cmp(j,B[1],1),i==cnt && j==up);//j==up && up==C[cnt]
        }
        return ret;
    }
    int main()
    {
        freopen("reverse.in","r",stdin);
        freopen("reverse.out","w",stdout);
        int T,a,b; usll l,r;
        scanf("%d%d%d",&T,&a,&b);
        while(T--){
            cin>>l>>r;
            memset(A,0,sizeof(A)); memset(B,0,sizeof(B)); memset(C,0,sizeof(C));
            init(l,A,ltot); init(r,B,rtot);
            init(r,C,cnt);  
            usll ans=solve();
            init(l-1,C,cnt),ans-=solve();
            cout<<ans<<endl;
        }
    }
    /*
    3 0 0
    1 10
    10 20
    123 12345
    
    7 0 1
    
    83 9503
    
    83 23773
    1 14605
    22 25083
    10 27604
    
    */
    View Code
  • 相关阅读:
    排列组合算法
    C++内存管理——堆&&栈
    编程之美——1.2 中国象棋将帅问题
    Gentoo: fcitx的安装
    Gentoo NTFS USB盘有写权限
    Gentoo U盘无法自动挂载,打开报告Not Authorized,xfce只有logout,suspend/shutdown灰化等问题解决方法
    Kernel: 打开CONFIG_EMBEDDED从而使更多的kernel option可以更改
    Gentoo Enable framebuffer console (没有安装X,KDE的时候)
    转载:Gentoo和Ubuntu包管理命令对比集
    Gentoo Rebuild virtualboxmodules when kernel is updated
  • 原文地址:https://www.cnblogs.com/mowanying/p/11735321.html
Copyright © 2011-2022 走看看