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 */
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 */
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 */