CSU 1810: Reverse

本文介绍了解决特定数学问题的有效算法,通过分析问题特点并优化计算流程,实现了从O(n^2)到O(n)的时间复杂度降低。文章详细阐述了核心思路及实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Description

Bobo has a n digits decimal number D=d1 d2…dn (It may have leading zeros).
Let R(i,j) denotes number D with digits between the i-th position and j-th position reversed. That is, R(i,j)=d1…di-1 dj dj-1…di dj+1 dj+2…dn.
Bobo would like to find
modulo (109+7).

Input

The input contains at most 30 sets. For each set:
The first line contains an integer n (1≤n≤105).
The second line contains n digits d1 d2…dn (0≤di≤9).

Output

For each set, an integer denotes the result.

思路:

只要考虑每一位数字他可能变换到其他位置的所有情况和对应方案数,乘起来得到这一位数字在所有Reverse中的贡献;将初始数码每一位的贡献求和即为答案。
很容易想到两个for循环:第一个循环枚举初始数码每一位i,第二个循环枚举其对应可能变到的位置j,O(n^2)。但第二个循环我们很快发现里面存在某种规律:那就是靠近当前位i的连续的一些位置,他们的贡献方案数是相同的,而剩下的位置方案数是递减的,这和i,j到两边的距离有关,所以只要分类讨论即可。预处理需要的量,省去第二个循环,最后复杂度降为O(n)。

比赛的时候推出了关系式,但被min(x,y)函数卡住了。
其实也没什么,只要分x<y,x>=y两类讨论就可以了,这样min(x,y)成了一个固定的表达式,就可以通过预处理解决了。

自己的手稿,不要嫌弃。。。。。。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e5+10;
const int mo=1e9+7;
char c[maxn];
long long a[maxn];
long long pre[maxn],s[maxn],t[maxn],p10[maxn];
long long C(long long n,long long m){
  if (n<m) return 0;
  return (n*(n-1)/2)%mo;
}
 
int main()
{
    int n,i,j;
    long long mul;
    long long ans,temp,sup;
    while (scanf("%d",&n)!=-1){
        scanf("%s",c);
        for (i=0;i<n;i++) a[i]=c[i]-'0';
        pre[0]=1;mul=1;s[0]=1;t[0]=0;p10[0]=1;
        for (i=1;i<=n-1;i++) {
            p10[i]=mul=(mul*10)%mo;
            pre[i]=(pre[i-1]+mul)%mo;
            s[i]=(s[i-1]+(i+1)*mul%mo)%mo;
            t[i]=(t[i-1]+(n-i)*mul%mo)%mo;
        }
 
        ans=0;
        for (i=0;i<n;i++){
           ans=(ans+a[i]*(min(i,n-i-1)+1+C(i,2)+(n-1)+C(n-i-1,2))%mo*p10[n-i-1]%mo)%mo;
           //0~i-1
           if (i-1>=0){
           if (n-i-1<=i-1){
              temp=((a[i]*(n-i))%mo)*(pre[i]-pre[n-i-1]+mo)%mo;
              ans=(ans+temp)%mo;
           }
           if (0<=min(n-i-2,i-1)){
              sup= i-1<n-i-2 ? n-i-1 : i;
              temp=a[i]*(t[n-1]-t[sup]+mo)%mo;
              ans=(ans+temp)%mo;
           }
           }
           //i+1~n-1
           if (i+1<=n-1){
           if (i+1<=n-i-1){
              temp=((a[i]*(i+1))%mo)*(i==0 ? pre[n-i-2]:pre[n-i-2]-pre[i-1]+mo)%mo;
              ans=(ans+temp)%mo;
           }
           if (max(n-i,i+1)<=n-1){
              sup= i+1>n-i ? n-i-2:i-1 ;
              temp=a[i]*s[sup]%mo;
              ans=(ans+temp)%mo;
           }
           }
        }
 
        printf("%lld\n",ans);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1810
    User: xushu
    Language: C++
    Result: Accepted
    Time:136 ms
    Memory:5704 kb
****************************************************************/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值