机房测试模拟2:模拟+数学+数位dp

旧巷老猫 提交于 2019-12-02 06:50:55

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\n",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\n"); continue; }
        printf("%lld\n",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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!