[NOI2019]机器人

机器人

题解

首先对于这道题,我们是比较容易得到一个 O ( n 2 B ) O\left(n^2B\right) O(n2B) d p dp dp做法的。
我们定义 d p l , r , k dp_{l,r,k} dpl,r,k表示对于区间 [ l , r ] [l,r] [l,r],其中最大值为 k k k,且机械人不能走出区间 [ l , r ] [l,r] [l,r]的方案数。
其中转移我们可以考虑枚举最后一个大小为 k k k的机械人在哪里,显然,它前面的机械人往后走都是不可能超过它的,它后面的也不可能走到它前面去,这样整个区间就被分成两半了,可以区间 d p dp dp
由于对于任意一个区间,为了满足两边距离差的绝对值不超过 2 2 2,可以放机械人的位置只会有常数个。
只需要再同时维护一个前缀和,就可以 O ( n 2 B ) O\left(n^2B\right) O(n2B)了。
但有用的区间真的有 O ( n 2 ) O(n^2) O(n2)吗?题目奇怪的性质好像会使绝大多数区间都不会贡献到答案。
经代码验证,真正有用的区间不会超过 3000 3000 3000个,这样就大大提升了我们代码运行的效率,基本已经可以过前 50 50 50分。

关注之后 A = 1 , B = 1 0 9 A=1,B=10^9 A=1,B=109的部分分。
我们不妨理性猜测这是一个以 [ A , B ] [A,B] [A,B]为值域的多项式,考虑归纳证明。
显然,对于 i = j i=j i=j的情况, d p i , j ( x ) = 1 dp_{i,j}(x)=1 dpi,j(x)=1
再将它做个前缀和,多项式的前缀和不也是多项式?下面用 d p ′ dp' dp表示做了前缀和了的 d p dp dp
再来看 d p l , r dp_{l,r} dpl,r的转移式, d p l , r ( x ) = ∑ m i d [ ∣ ( r − m i d ) − ( m i d − l ) ∣ ⩽ 2 ] d p l , m i d − 1 ′ ( x ) d p m i d + 1 , r ′ ( x ) dp_{l,r}(x)=\sum_{mid}[|(r-mid)-(mid-l)|\leqslant 2]dp'_{l,mid-1}(x)dp'_{mid+1,r}(x) dpl,r(x)=mid[(rmid)(midl)2]dpl,mid1(x)dpmid+1,r(x),这不就是把递归到两边的多项式乘起来吗?这不还是一个多项式?
于是我们就完成了证明。
同时通过上面的递归过程,我们可以知道 d p l , r dp_{l,r} dpl,r是个 r − l r-l rl次的多项式, d p ′ dp' dp自然是 r − l + 1 r-l+1 rl+1次的多项式。
然后就可以拉格朗日插值了。

有了上面的经验,自然可以想到对于之后的点我们将 [ A , B ] [A,B] [A,B]进行离散化,并在离散化后的每个连续区间内, d p l , r dp_{l,r} dpl,r都是一个多项式。
这是显然的,毕竟在这区间外的位置的答案对里面的贡献都只是一个常数,并不会影响它是不是一个多项式。
那么我们只需要对于离散化后得到的值域区间依次对整个序列区间 d p dp dp求解,并在之后保留下来上一次求解得到的前缀和以辅助下一次 d p dp dp求解,就能够很快地得到整个区间的答案了。
另外,如果插值的时候当前值域区间不足 r − l + 2 r-l+2 rl+2个,我们可以只插值域区间长度这么多的值,毕竟这样就可以应付我们所有的询问了。

m m m为有用区间长度的平方和(显然是比较小的),最后总时间复杂度为 O ( m n ) O\left(mn\right) O(mn)

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
#define MAXN 4005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=1e9+7;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const LL INF=0x3f3f3f3f3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,a[305],b[305],d[605],totd,ls[MAXN],rs[MAXN];
int g[MAXN][605],id[305][305],idx;pii cp[305];bool vis[MAXN][605];
struct poly{
    int m,a[305];poly(){m=0;}
    void clear(){for(int i=0;i<=m;i++)a[i]=0;}
    poly operator + (const poly &rhs)const{
        poly res;res.m=max(m,rhs.m);res.clear();
        for(int i=0;i<=m;i++)res.a[i]=a[i];
        for(int i=0;i<=rhs.m;i++)Add(res.a[i],rhs.a[i],mo);
        return res;
    }
    poly operator * (const int &rhs)const{
        poly res;res.m=m;res.clear();
        for(int i=0;i<=m;i++)res.a[i]=1ll*rhs*a[i]%mo;
        return res;
    }
    poly operator ^ (const int &rhs)const{
        poly res;res.m=m+1;res.clear();
        for(int i=0;i<=m;i++)res.a[i]=mo-1ll*rhs*a[i]%mo;
        for(int i=0;i<=m;i++)Add(res.a[i+1],a[i],mo);
        return res;
    }
    int ask(int x){
        int res=0,now=1;
        for(int i=0;i<=m;i++,now=1ll*x*now%mo)
            Add(res,1ll*a[i]*now%mo,mo);
        return res;
    }
}dp[MAXN],TP;
void init(int l,int r){
    if(id[l][r])return ;
    id[l][r]=++idx;dp[idx].m=dp[idx].a[0]=0;
    if(l==r)return ;int rt=id[l][r];
    for(int i=l;i<=r;i++)
        if(Fabs((i-l)-(r-i))<=2){
            if(l<i)init(l,i-1);
            if(r>i)init(i+1,r);
            if(!ls[rt])ls[rt]=i;rs[rt]=i;
        }
}
void sakura(int l,int r,int p){
    int rt=id[l][r];if(vis[rt][p])return ;vis[rt][p]=1;
    if(l==r){
        if(a[l]>p)dp[rt].m=0,dp[rt].a[0]=0,g[rt][p]=0;
        else if(a[l]<=p&&p<b[l])
            dp[rt].m=1,dp[rt].a[1]=1,dp[rt].a[0]=mo-d[a[l]]+1,g[rt][p]=d[p+1]-d[a[l]];
        else dp[rt].m=0,dp[rt].a[0]=d[b[l]]-d[a[l]],g[rt][p]=d[b[l]]-d[a[l]];
        return ;
    }
    for(int i=ls[rt];i<=rs[rt];i++){
        if(l<i)sakura(l,i-1,p);
        if(r>i)sakura(i+1,r,p);
    }
    int len=min(d[p+1]-d[p],r-l+2),summ=g[rt][p-1];
    for(int i=1,now=d[p];i<=len;i++,now++){
        for(int j=ls[rt],tmp=1;j<=rs[rt];j++){
            if(d[a[j]]>now||now>=d[b[j]])continue;
            if(l<j)tmp=1ll*tmp*dp[id[l][j-1]].ask(now)%mo;
            if(j<r)tmp=(now==d[p]?1ll*tmp*g[id[j+1][r]][p-1]:1ll*tmp*dp[id[j+1][r]].ask(now-1))%mo;
            Add(summ,tmp,mo);tmp=1;
        }
        cp[i]=mkpr(summ,now);
    }
    dp[rt].m=0;dp[rt].clear();dp[rt].a[0]=cp[1].fir;
    TP.m=1;TP.clear();TP.a[1]=1;TP.a[0]=mo-cp[1].sec;
    for(int i=2;i<=len;i++){
        int tp=add(cp[i].fir,mo-dp[rt].ask(cp[i].sec),mo);
        if(tp)tp=1ll*tp*qkpow(TP.ask(cp[i].sec),mo-2,mo)%mo;
        if(tp)dp[rt]=dp[rt]+TP*tp;TP=TP^cp[i].sec;
    }
    g[rt][p]=dp[rt].ask(d[p+1]-1);bool flag=0;
}
int main(){
    read(n);init(1,n);
    for(int i=1;i<=n;i++)
        read(a[i]),read(b[i]),
        d[++totd]=a[i],d[++totd]=b[i]+1;
    sort(d+1,d+totd+1);totd=unique(d+1,d+totd+1)-d-1;
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(d+1,d+totd+1,a[i])-d,
        b[i]=lower_bound(d+1,d+totd+1,b[i]+1)-d;
    for(int i=1;i<totd;i++)sakura(1,n,i);
    printf("%d\n",g[id[1][n]][totd-1]);
    return 0;
}

谢谢!!!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值