[APIO2016] 划艇

https://www.luogu.com.cn/problem/P3643

https://loj.ac/p/2567

还在卡常,写了两天,醍醐灌顶的好题!

前言:感谢 wxy 老师的耐心指导。若有 F(x)G(x)modp,我们要求出 F(R)modp,但是给定的点值为 (x,F(x)modp) 的形式,也就是给出的是 (x,G(x)),那我们插值还是正确的吗?考虑插值的公式 H(x)=iyikixxkxixk,那么现在给定的并不是 yi,而是 yimodpxk,给定的也是 xkmodp,我们还能求出正确的答案吗?注意到本质上求的是 H(x)modp,那么你对于各个部分先取模,显然最后得到的也是对的。所以本质上我们并不是在插 G(x),仍然可看作在插 F(x),只是插最后的答案要 modp 而已。因此,我们仍然需要插 deg(F(x))+1 个点。

问题难点在于加速如下方程。

f(i,j)={k=1jf(i1,k),aijbif(i1,j),otherwise

这里别当成函数,当成 f 数组。

我把第一个方程的操作称为类前缀和。

初始化为

f0(0)=1

f0(k)=0,k0

我认为难点在于如何理解它是个 fi 是个函数,且函数表达式为一个多项式,当然,有可能是个分段函数。

可通过数归来分析。

  1. 初始,可看成 f0(x)={1,x=00,otherwise,显然这是个分段函数,且每个函数表达式都是一个次数界为 1 的多项式。

  2. 转移,考虑到 i 的时候,fi 可能已经被分成了若干段,各段均为一个表达式是多项式的函数,image

    如图,那么,考虑 ai+1,bi+1 搞下去,image
    会分裂常数个函数,也就是会新增常数个段,那么绿色这种完整覆盖的,显然其函数只是相当于做了一个类前缀和操作,但其定义域是没发生变化的,但其次数会 +1,下面会具体分析这个操作,对于紫色的,没被覆盖到,显然啥都没变化。对于红色,蓝色的,会其被棕色覆盖到的部分,会进行一次类前缀和的操作,因此其函数会不同于原先。

  3. 对一个多项式函数施类前缀和操作,只会让其次数 +1,并不会改变其是一个多项式的事实。
    image
    考虑对蓝色块进行前缀和,那么显然其 F(x)=C+kxf(x),其中 C 为红色段与绿色段的和,然后这个东西升次了,可以用有限微积分来分析,当然积分直觉性地理解也可以,若有 G(x)=F(x)dx,则 deg(G)=deg(F)+1,然后对一个多项式积分之后显然还是一个多项式。

  4. 类前缀和操作只会操作整块。我们可以离散化端点,这样区间内的每个点受到的操作都是一样的了。

那么,到最后,fn 一定是一个由 O(n) 个次数界为 n+1 的多项式组成的分段函数。

考虑咋做,对于离散化后的区间 [l,r],设其函数为 Fi(x),这里有下标是因为我们要对于每个 i,求出 f(i,j),因此其每个 i 的函数都不一样。那么为了转移,我们要求出 k=lrF(k),那么显然要求出来 f(i,k),0in,lkl+n,即插 n+1 个点,然后通过拉格朗日插值求出来,但是不是需要多点求值!显然,你再维护一个 g(i,j)=lkjf(i,k),即可,又因为这个操作等价于类前缀和,所以 Gi(x) 为对 Fi(x) 求前缀和,注意,二者定义域都在 [l,r],插 n+2 个点就能求出来了。然后一些精细实现,本题难点并不在这。

一些实现注意点:

  1. 虽然插值共有 O(n2) 次,但每 O(n) 次插的点的 x 都是一样的,因此可以预处理 ,单次均摊 O(n)

  2. 因为我们对于每个 x,预先处理了 xxk,因此 xxi 必须存在逆元,即其不为 0,否则的话可以特判规避下,即若区间长度 <=n+1 直接暴力,否则直接插,因为 p 很大,因此我们这样是对的。

#include <bits/stdc++.h> //#define int long long #define pb push_back #define ADD(x,y) (((x)+(y)>=mod?(x)+((y)-mod):((x)+(y)))) using namespace std; const int N=520,mod=(int)(1e9+7),M=(int)(5e6+5); int f[N][N],g[N][N]; int sum[N],lsh[N*100],tot,a[N],b[N],k,n; int fpow(int x,int y) { int res=1; x%=mod; while(y) { if(y&1) res=1ll*res*x%mod; y>>=1; x=1ll*x*x%mod; } return res; } int X[N],Y[N],cnt,p[N],q[N],pre[N],r1; int F(int x) { // cout<<"usd"; int res=0; // for(int i=cnt;i>=1;i--) suf[i]=suf[i+1]*i%mod; for(int i=1;i<=cnt;i++) { if(p[i]==-1) { p[i]=q[i]=1; p[i]=1ll*r1*fpow(x-X[i],mod-2)%mod; if(i) q[i]=pre[i-1]; if(i<cnt) { q[i]=1ll*q[i]*pre[cnt-i]%mod; if((cnt-i)&1) q[i]=mod-q[i]; //// for(int j=i+1;j<=cnt;j++) q[i]=1ll*q[i]*(i-j)%mod; } // for(int j=1;j<=cnt;j++) // if(i!=j) p[i]=1ll*p[i]*(x-X[j])%mod,q[i]=1ll*q[i]*(X[i]-X[j])%mod; p[i]=(p[i]%mod+mod)%mod; // q[i]=(q[i]%mod+mod)%mod; q[i]=fpow(q[i],mod-2); q[i]=(q[i]%mod+mod)%mod; p[i]=1ll*p[i]*q[i]%mod; p[i]=(p[i]%mod+mod)%mod; } // } // p=(p%mod+mod)%mod; q=(q%mod+mod)%mod; res=ADD(res,1ll*Y[i]*p[i]%mod); } return res; } signed main() { // freopen("2.in","r",stdin); cin.tie(0); ios::sync_with_stdio(false); cin>>n; // int mx=0; pre[0]=1; for(int i=1;i<=n+10;i++) pre[i]=1ll*pre[i-1]*i%mod; // pinv[0]=1; for(int i=1;i<=n;i++) pinv[i]=1ll*pinv[i-1]*fpow(i,mod-2)%mod; // for(int i=0;i<=M-5;i++) inv[i]=fpow(i,mod-2); for(int i=1;i<=n;i++) { cin>>a[i]>>b[i]; lsh[++tot]=a[i]; lsh[++tot]=b[i]; lsh[++tot]=max(0,a[i]-1); // lsh[++tot]=a[i]+1; // mx=max(mx,b[i]); // lsh[++tot]=max(0ll,b[i]-1); lsh[++tot]=b[i]+1; } f[0][0]=1; lsh[++tot]=0; // lsh[++tot]=mx+1; // lsh[++tot]=mx+1; lsh[++tot]=1; stable_sort(lsh+1,lsh+1+tot); tot=unique(lsh+1,lsh+1+tot)-lsh-1; for(int i=1;i<tot;i++) { int l=lsh[i],r=lsh[i+1]-1; //[,) if(r<=l+n) { for(int j=0;j<=n;j++) { if(a[j]<=l&&r<=b[j]) { if(j) for(int k=l;k<=r;k+=4) { f[j][k-l]=ADD(g[j-1][k-l],sum[j-1]); if(k+1<=r) f[j][k-l+1]=ADD(g[j-1][k-l+1],sum[j-1]); if(k+2<=r) f[j][k-l+2]=ADD(g[j-1][k-l+2],sum[j-1]); if(k+3<=r) f[j][k-l+3]=ADD(g[j-1][k-l+3],sum[j-1]); } } else { if(j) for(int k=l;k<=r;k+=4) { f[j][k-l]=f[j-1][k-l]; if(k+1<=r) f[j][k-l+1]=f[j-1][k-l+1]; if(k+2<=r) f[j][k-l+2]=f[j-1][k-l+2]; if(k+3<=r) f[j][k-l+3]=f[j-1][k-l+3]; } } for(int k=l;k<=r;k+=4) { g[j][k-l]=f[j][k-l]; if(k>l) g[j][k-l]=ADD(g[j][k-l],g[j][k-1-l]); if(k+1<=r) { g[j][k-l+1]=f[j][k-l+1]; if(k+1>l) g[j][k-l+1]=ADD(g[j][k-l+1],g[j][k-1-l+1]); } if(k+2<=r) { g[j][k-l+2]=f[j][k-l+2]; if(k+2>l) g[j][k-l+2]=ADD(g[j][k-l+2],g[j][k-1-l+2]); } if(k+3<=r) { g[j][k-l+3]=f[j][k-l+3]; if(k+3>l) g[j][k-l+3]=ADD(g[j][k-l+3],g[j][k-1-l+3]); } } } for(int j=0;j<=n;j++) { for(int k=l;k<=r;k+=4) { sum[j]=ADD(sum[j],f[j][k-l]); if(k+1<=r) sum[j]=ADD(sum[j],f[j][k-l+1]); if(k+2<=r) sum[j]=ADD(sum[j],f[j][k-l+2]); if(k+3<=r) sum[j]=ADD(sum[j],f[j][k-l+3]); } } for(int j=0;j<=n;j++) { // for(int k=0;k<=r-l;k++) f[j][k]=g[j][k]=0; memset(f[j],0,sizeof(int)*(r-l+1)); memset(g[j],0,sizeof(int)*(r-l+1)); } // f[0][0]=1; } else { int R=l+n; for(int j=0;j<=n;j++) { if(a[j]<=l&&R<=b[j]) { if(j) for(int k=l;k<=R;k+=4) { f[j][k-l]=ADD(g[j-1][k-l],sum[j-1]); if(k+1<=R) f[j][k-l+1]=ADD(g[j-1][k-l+1],sum[j-1]); if(k+2<=R) f[j][k-l+2]=ADD(g[j-1][k-l+2],sum[j-1]); if(k+3<=R) f[j][k-l+3]=ADD(g[j-1][k-l+3],sum[j-1]); } } else { if(j) for(int k=l;k<=R;k+=4) { f[j][k-l]=f[j-1][k-l]; if(k+1<=R) f[j][k-l+1]=f[j-1][k-l+1]; if(k+2<=R) f[j][k-l+2]=f[j-1][k-l+2]; if(k+3<=R) f[j][k-l+3]=f[j-1][k-l+3]; } } for(int k=l;k<=R;k+=4) { g[j][k-l]=f[j][k-l]; if(k>l) g[j][k-l]=ADD(g[j][k-l],g[j][k-1-l]); if(k+1<=R) { g[j][k-l+1]=f[j][k-l+1]; if(k+1>l) g[j][k-l+1]=ADD(g[j][k-l+1],g[j][k-1-l+1]); } if(k+2<=R) { g[j][k-l+2]=f[j][k-l+2]; if(k+2>l) g[j][k-l+2]=ADD(g[j][k-l+2],g[j][k-1-l+2]); } if(k+3<=R) { g[j][k-l+3]=f[j][k-l+3]; if(k+3>l) g[j][k-l+3]=ADD(g[j][k-l+3],g[j][k-1-l+3]); } } } for(int j=1;j<=R-l+1;j++) p[j]=q[j]=-1; cnt=R-l+1; r1=1; for(int j=1;j<=cnt;j++) X[j]=j+l-1,r1=1ll*r1*(r-X[j])%mod; for(int j=0;j<=n;j++) { for(int k=l;k<=R;k++) Y[k-l+1]=g[j][k-l]; sum[j]=ADD(sum[j],F(r)); } for(int j=0;j<=n;j++) { // for(int k=0;k<=R-l;k++) f[j][k]=g[j][k]=0; memset(f[j],0,sizeof(int)*(R-l+1)); memset(g[j],0,sizeof(int)*(R-l+1)); } // for(int j=0;j<=n;j++) { // unordered_map<int,int>().swap(f[j]); // unordered_map<int,int>().swap(g[j]); // } // f[0][0]=1; } } // for(int i=0;i<=mx;i++) cout<<f[n][i]<<' '; // cout<<g[n][mx]-1<<'\n'; int ans=((sum[n]-1)%mod+mod)%mod; cout<<ans; return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/17087749.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示