【HNOI2019】部分题简要题解

时间:2022-02-11 15:58:20

题意懒得写了

LOJ

Day 1 T1 鱼

个人做法比较猎奇,如果有哪位大佬会证明能分享一下的话感激不尽。

题解:枚举鱼尾和鱼身的交点D,将所有其他点按照到D的距离排序,距离相同的分一组。

感性的理解,对于每个点D,暴力枚举距离相等的点对(B,C)。这样总的数量不会很多。感觉仍然是\(O(n^2)\)级别的。

那么我们对枚举的D,将所有的点对的中垂射线和点按照极角排序,扫一圈就能得到答案了。鱼尾的部分也是利用扫描线,用叉积判断可能会有问题(转过了180度),那么我们可以将其倍长,用极角的值来判即可。

精度要求比较高。

奇丑无比...

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 2005
#define M 1000005
#define LL long long
#define LT long double
using namespace std;
int n;
LL ans;
//geometry
long double sqr(LL x)
{
LT v=x;
return v*v;
}
namespace geometry
{
const long double pi=acos(-1);
const long double eps=1e-15;
struct node
{
LL x,y;
node(LL _x=0,LL _y=0){x=_x,y=_y;}
};
node operator +(node x,node y) {return node(x.x+y.x,x.y+y.y);}
node operator -(node x,node y) {return node(x.x-y.x,x.y-y.y);}
long double angel(node x)
{
long double v=atan2(x.y,x.x);
if(v<0) v+=2*pi;
return v;
}
LT dis2(node x,node y) {return sqr(x.x-y.x)+sqr(x.y-y.y);}
LT crs(node x,node y)
{
return (LT)x.x*(LT)y.y-(LT)x.y*(LT)y.x;
}
}
using namespace geometry; node d[N],a[N],dw[M];
LL c1[N],cnt[N];
LT ds[N];
int n1,px[N],fr[N],l,le;
vector<int> pt[N]; long double ag(int x)
{
if(x>l) return angel(d[px[x]])+2*pi;
else return angel(d[px[x]]);
}
int pw(node x)
{
if(x.x>=0) return (x.y>=0)?1:4;
else return (x.y>=0)?2:3;
}
bool cmp(int x,int y) {return ds[x]<ds[y];}
bool cmp2(node x,node y)
{
int fx=pw(x),fy=pw(y);
return (fx<fy||(fx==fy&&crs(x,y)>0));
}
bool cmp3(int x,int y) {return cmp2(d[x],d[y]);}
bool cmp4(node x,node y) {return dis2(x,node(0,0))<dis2(y,node(0,0));} bool eql(node x,node y)
{
return(!cmp2(x,y)&&!cmp2(y,x));
}
int ed[N],mx,mn; LL query(int k)
{
l=0; fo(i,1,n)
if(i!=k)
{
d[++l]=a[i]-a[k];
px[l]=l;
ds[l]=dis2(a[i],a[k]);
}
sort(px+1,px+l+1,cmp);
n1=0;
fo(i,1,l)
{
if(i==1||abs(ds[px[i]]-ds[px[i-1]])>eps)
{
cnt[++n1]=0;
pt[n1].clear();
}
cnt[n1]++,pt[n1].push_back(px[i]),fr[px[i]]=n1;
}
if(l<5) return 0;
le=0;
fo(i,1,n1)
{
fo(j,0,cnt[i]-1)
{
fo(k,j+1,cnt[i]-1)
{
int u=pt[i][j],v=pt[i][k];
if((d[u]+d[v]).x!=0||(d[u]+d[v]).y!=0) dw[++le]=d[u]+d[v];
}
}
}
memset(c1,0,sizeof(c1));
sort(px+1,px+l+1,cmp3);
sort(dw+1,dw+le+1,cmp2);
int lst=0;
fo(i,1,l)
{
if(i==1||!eql(d[px[i]],d[px[i-1]]))
{
if(lst!=0)
{
sort(px+lst,px+i,cmp);
fo(j,lst,i-1) ed[j]=i-1;
}
lst=i;
}
}
if(lst!=0)
{
sort(px+lst,px+l+1,cmp);
fo(j,lst,l) ed[j]=l;
}
lst=0;
fo(i,1,le)
{
if(i==1||!eql(dw[i],dw[i-1]))
{
if(lst!=0) sort(dw+lst,dw+i,cmp4);
lst=i;
}
}
if(lst!=0) sort(dw+lst,dw+le+1,cmp4);
int lp=1,rp=1;
LL s1=0,sp=0;
fo(i,1,l) px[i+l]=px[i]; long double upg=angel(dw[1])+pi/2,dpg=angel(dw[1])+3*pi/2; while(lp<=2*l&&ag(lp)<upg+eps) lp++;
rp=lp;
while(rp<=2*l&&ag(rp)<dpg) rp++; for(int j=lp;j<rp;j++)
{
sp-=(LL)c1[fr[px[j]]]*(LL)(c1[fr[px[j]]]-1);
c1[fr[px[j]]]++;
sp+=(LL)c1[fr[px[j]]]*(LL)(c1[fr[px[j]]]-1);
} int j=1,ef=0;
while(j<=l&&cmp2(d[px[j]],dw[1])) j++;
if(j<=l&&eql(d[px[j]],dw[1])) ef=ed[j];
else ef=0; LT di=dis2(dw[1],node(0,0));
while(j<=ef&&di>=(LT)4*ds[px[j]]) j++;
if(ef!=0) s1=s1+sp*(LL)2*(LL)(ef-j+1);
mx=max(mx,le),mn=max(mn,n1); fo(i,2,le)
{
if(i==1||!eql(dw[i],dw[i-1]))
{
upg=angel(dw[i])+pi/2,dpg=angel(dw[i])+3*pi/2;
while(rp<=2*l&&ag(rp)<dpg-eps)
{
sp-=(LL)c1[fr[px[rp]]]*(LL)(c1[fr[px[rp]]]-1);
c1[fr[px[rp]]]++;
sp+=(LL)c1[fr[px[rp]]]*(LL)(c1[fr[px[rp]]]-1);
rp++;
} while(lp<=2*l&&ag(lp)<upg+eps)
{
sp-=(LL)c1[fr[px[lp]]]*(LL)(c1[fr[px[lp]]]-1);
c1[fr[px[lp]]]--;
sp+=(LL)c1[fr[px[lp]]]*(LL)(c1[fr[px[lp]]]-1);
lp++;
} while(j<=l&&cmp2(d[px[j]],dw[i])) j++;
if(j<=l&&eql(d[px[j]],dw[i])) ef=ed[j];
else ef=0;
}
LT di=dis2(dw[i],node(0,0)); while(j<=ef&&di>=(LT)4*ds[px[j]]) j++;
if(ef!=0) s1=s1+sp*(LL)2*(LL)(ef-j+1);
} return s1;
}
int main()
{
cin>>n;
fo(i,1,n) scanf("%lld%lld\n",&a[i].x,&a[i].y);
ans=0;
fo(i,1,n)
{
ans+=query(i);
}
printf("%lld\n",ans);
}

Day 1 T3 多边形

题解:观察样例可以发现,最终状态一定是n-3条边都与n号点相连。

那么次数最少的操作一定每一次都是要连到n的

进一步可以发现,某些边的操作一定在某条边之后,先后关系可以构成一个森林。

操作次数就是森林点数,方案数就是森林的拓扑序种数。

现在提前给出一些操作,观察这些操作在树上的变化,要么直接删去一个点,要么改变一些父子关系,改变的个数只有常数个,直接计算这些点对答案的影响即可。

用map来存标号,时间复杂度\(O(n\log n)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define mo 1000000007
#define LL long long
using namespace std;
int mx[N],mi[N],n,m,n1,tp,ft[N],fn[N],t[N][2],ap[N][2],sz[N];
map<int,int> h[N];
LL f[N],js[N],ns[N],ny[N];
bool bz[N]; LL C(int n,int m)
{
if(n<m) return 0;
return js[n]*ns[m]%mo*ns[n-m]%mo;
}
LL nC(int n,int m)
{
if(n<m) return 0;
return ns[n]*js[m]%mo*js[n-m]%mo;
} void dfs(int k)
{
if(!k) return;
dfs(t[k][0]),dfs(t[k][1]);
sz[k]+=sz[t[k][0]]+sz[t[k][1]];
f[k]=f[t[k][0]]*f[t[k][1]]%mo*C(sz[k]-1,sz[t[k][0]])%mo;
} LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
int main()
{
freopen("polygon.in","r",stdin);
freopen("polygon.out","w",stdout);
cin>>tp;
cin>>n;
js[0]=ns[0]=js[1]=ns[1]=ny[1]=1;
fo(i,2,n) js[i]=js[i-1]*(LL)i%mo,ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;
fo(i,2,n) ns[i]=ns[i-1]*ny[i]%mo;
memset(mi,107,sizeof(mi));
fo(i,1,n-3)
{
int x,y;
scanf("%d%d",&x,&y);
if(x>y) swap(x,y);
if(y==n) {bz[i]=1;continue;}
ap[i][0]=x,ap[i][1]=y;
h[x][y]=i,h[y][x]=i;
mx[x]=max(mx[x],y),mx[y]=max(mx[y],x);
mi[x]=min(mi[x],y),mi[y]=min(mi[y],x);
}
fo(i,1,n-3)
{
int x=ap[i][0],y=ap[i][1];
if(bz[i]) continue;
n1++,sz[i]=1;
if(mx[x]>y)
{
int w=(*h[x].upper_bound(y)).second;
if(bz[w]) continue;
ft[i]=w,fn[i]=0,t[w][0]=i;
}
else if(mi[y]<x)
{
map<int,int>::iterator it=h[y].find(x);it--;
int w=(*it).second;
if(bz[w]) continue;
ft[i]=w,fn[i]=1,t[w][1]=i;
}
}
f[0]=1;
LL ans=1,sp=0;
fo(i,1,n-3)
if(!bz[i]&&!ft[i])
{
dfs(i);
sp+=sz[i];
ans=ans*f[i]%mo*C(sp,sz[i])%mo;
}
printf("%d",n1);
if(tp) printf(" %lld",ans);
printf("\n");
cin>>m;
fo(i,1,m)
{
int x,y,w;
scanf("%d%d",&x,&y);
w=h[x][y];
int n2=n1;LL s1=ans;
if(!ft[w])
{
n2--;
s1=s1*nC(sp,sz[w])%mo*ksm(f[w],mo-2)%mo;
s1=s1*C(sp-1-sz[t[w][0]],sz[t[w][1]])%mo*f[t[w][1]]%mo;
s1=s1*C(sp-1,sz[t[w][0]])%mo*f[t[w][0]]%mo;
}
else
{
int r=ft[w],p=fn[w];
if(p!=0) printf("WA\n");
s1=s1*ksm(f[r],mo-2)%mo;
s1=s1*f[t[w][1]]%mo*f[t[r][1]]%mo*C(sz[t[w][1]]+sz[t[r][1]],sz[t[w][1]])%mo*f[t[w][0]]%mo*C(sz[r]-1,sz[t[w][0]])%mo;
}
printf("%d",n2);
if(tp) printf(" %lld",s1);
printf("\n");
}
}

Day 2 T1 校园旅行

题解:考虑一个暴力DP,\(f[x][y]\)表示x,y是否能通过回文串到达,枚举两边的出边,按照BFS的顺序转移,时间复杂度\(O(m^2)\)

考虑优化,我们将边分类,要么是连接不同颜色的边,要么是连接相同颜色的边。

只取出连接相同颜色的边,考虑每一个连通块,如果它是个二分图,那么保留一棵生成树即可(因为匹配配只与奇偶性有关,数量不同可以在一条边反复横跳满足)。如果不是,那么奇偶性可以改变,可以任意匹配,那么保留一棵生成树,再连上一个自环表示奇环的情况。对于连接不同颜色的边的联通块,它显然是二分图,直接保留生成树即可。

连边采用并查集,时间复杂度\(O(m\alpha(n))\)

这样边数也降为了\(O(n)\)级别,暴力DP的时间复杂度就变成了\(O(n^2)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 5005
#define L 25000005
using namespace std;
int q,n,m,c[N],cnt,lp,m1;
bool f[N][N];
int d[L][2],f1[N],g[N],a[N][2][N],le;
bool bp[N],bc[N];
void bfs()
{
fo(i,1,n) f[i][i]=1,d[++le][0]=i,d[le][1]=i;
int l=0,r=le;
while(l<r)
{
l++;
int x=d[l][0],y=d[l][1];
fo(e,0,1)
{
fo(i,1,a[x][e][0])
{
int u=a[x][e][i];
fo(j,1,a[y][e][0])
{
int v=a[y][e][j];
if(!f[u][v]) f[u][v]=f[v][u]=1,d[++r][0]=u,d[r][1]=v;
}
}
}
}
n++,n--;
}
int getf(int k)
{
if(f1[k]==k||!f1[k]) return k;
int p=getf(f1[k]);
bc[k]^=bc[f1[k]];
return f1[k]=p;
}
int getf1(int k)
{
if(g[k]==k||!g[k]) return k;
return g[k]=getf1(g[k]);
}
void link(int x,int y)
{
a[x][c[y]][++a[x][c[y]][0]]=y;
}
int main()
{
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
cin>>n>>m>>q;
scanf("\n");
fo(i,1,n)
{
char ch=getchar();
c[i]=ch-'0';
}
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
if(c[x]==c[y])
{
int fx=getf(x),fy=getf(y);
if(fx!=fy)
{
f1[fy]=fx,bp[fx]|=bp[fy],bc[fy]=bc[x]^1^bc[y]^bc[fx];
link(x,y),link(y,x);
f[x][y]=f[y][x]=1;
d[++le][0]=x,d[le][1]=y;
}
else if(bc[x]==bc[y]) bp[fx]=1;
}
else
{
int fx=getf1(x),fy=getf1(y);
if(fx!=fy) g[fy]=fx,link(x,y),link(y,x);
}
}
fo(i,1,n) if(getf(i)==i&&bp[i]) link(i,i);
bfs();
fo(i,1,q)
{
int x,y;
scanf("%d%d",&x,&y);
if(f[x][y]) printf("YES\n");
else printf("NO\n");
}
}

Day 2 T2 白兔之舞

考虑枚举走了i步,容易得出\(Ans_t=\sum\limits_{i=0}^{L}[(i-t)\%k==0]{L\choose i}W^i_{x,y}\)

根据单位根反演的公式\([n|k]={1\over n}\sum\limits_{i=0}^{n-1}\omega_n^{ki}\)

这里单位根我们找一个p的原根,然后取其(p-1)/k次作为K次单位根

代入,交换主体可得

\(Ans_t=\sum\limits_{j=0}^{k-1}\omega_k^{-jt}\sum\limits_{i=0}^{L}{L\choose i}\left(\omega_k^j\right)^iW^i_{x,y}\)

后面的部分用二项式定理就是\(\left(\omega_k^jW+I\right)^L_{x,y}\),枚举j,用矩阵快速幂做

前面的\(\omega_k^{-jt}\),一个经典的套路就是将\(jt\)拆成\({j+t\choose 2}-{j\choose 2}-{t\choose 2}\)

这样剩下的就是一个卷积了,用任意模数NTT/MTT优化即可。

时间复杂度\(O(n^3k\log L+k\log k)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 3
#define M 262144
#define L 18
#define LL long long
using namespace std;
int n,m,r,st,ed,mo,P;
LL wp[M+1];
int d[100];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
bool pd(LL v)
{
fo(i,1,d[0]) if(ksm(v,(mo-1)/d[i])==1) return 0;
return 1;
} namespace polynomial
{
const double pi=acos(-1);
struct Z
{
double x,y;
Z(double _x=0,double _y=0){x=_x,y=_y;}
};
Z operator +(Z a,Z b) {return Z(a.x+b.x,a.y+b.y);}
Z operator -(Z a,Z b) {return Z(a.x-b.x,a.y-b.y);}
Z operator *(Z a,Z b) {return Z(a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y);}
Z wi[M+1],u1[M+1],u2[M+1],u3[M+1],u4[M+1],ux[M+1],uy[M+1];
int bit[M+1];
void prp()
{
fo(i,0,M)
{
bit[i]=(bit[i>>1]>>1)|((i&1)<<(L-1));
wi[i]=Z(cos(i*2*pi/M),sin(i*2*pi/M));
}
}
void DFT(Z *a,bool pd)
{
fo(i,0,M-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
Z v;
for(int h=1,m=2,l=M>>1;m<=M;l>>=1,h=m,m<<=1)
{
for(int j=0;j<M;j+=m)
{
Z *x=a+j,*y=a+j+h,*w=(!pd)?wi:wi+M;
fo(i,0,h-1)
{
Z v=*y * *w;
*y=*x-v,*x=*x+v;
x++,y++,w+=(!pd)?l:-l;
}
}
}
if(pd) fo(i,0,M-1) a[i].x/=M,a[i].y/=M;
}
void rec(Z *a,Z *x,Z *y)
{
fo(i,0,M-1)
{
x[i].x=(a[i].x+a[(M-i)%M].x)/2;
x[i].y=(a[i].y-a[(M-i)%M].y)/2;
y[i].x=(a[i].y+a[(M-i)%M].y)/2;
y[i].y=(a[(M-i)%M].x-a[i].x)/2;
}
}
void mul(LL *a,LL *b)
{
fo(i,0,M-1) ux[i]=uy[i]=u1[i]=u2[i]=u3[i]=u4[i]=Z(0,0);
fo(i,0,M-1)
{
ux[i].x=a[i]/P,ux[i].y=a[i]%P;
uy[i].x=b[i]/P,uy[i].y=b[i]%P;
}
prp();
DFT(ux,0),DFT(uy,0);
rec(ux,u1,u2),rec(uy,u3,u4);
fo(i,0,M-1)
{
ux[i]=u1[i]*u3[i]+(u1[i]*u4[i])*Z(0,1);
uy[i]=u2[i]*u4[i]+(u2[i]*u3[i])*Z(0,1);
}
DFT(ux,1),DFT(uy,1);
fo(i,0,M-1)
{
LL u=ux[i].x+mo+0.5,v=ux[i].y+mo+0.5,p=uy[i].y+mo+0.5,q=uy[i].x+mo+0.5;
u%=mo,v%=mo,p%=mo,q%=mo;
a[i]=((u*P%mo*P%mo+(v+p)%mo*P%mo+q)%mo+mo)%mo;
}
}
}
using namespace polynomial; namespace matrix
{
struct MT
{
LL a[3][3];
}ac,ua;
MT operator *(MT &a,MT &b)
{
MT c;
memset(c.a,0,sizeof(c.a));
fo(i,0,n-1)
fo(k,0,n-1)
fo(j,0,n-1) c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mo;
return c;
}
MT kst(MT &a,LL x)
{
MT s;
memset(s.a,0,sizeof(s.a));
fo(i,0,n-1) s.a[i][i]=1;
for(;x;x>>=1,a=a*a) if(x&1) s=s*a;
return s;
}
}
using namespace matrix;
LL a[M+1],b[M+1]; int main()
{
freopen("dance.in","r",stdin);
freopen("dance.out","w",stdout);
cin>>n>>m>>r>>st>>ed>>mo;
P=sqrt(mo);
fo(i,0,n-1) fo(j,0,n-1) scanf("%lld",&ac.a[i][j]);
int n1=mo-1;
fo(i,2,P)
{
if(n1%i==0)
{
d[++d[0]]=i;
while(n1%i==0) n1/=i;
}
}
wp[0]=1;
for(wp[1]=2;!pd(wp[1]);wp[1]++);
wp[1]=ksm(wp[1],(mo-1)/m);
fo(i,2,m) wp[i]=wp[i-1]*wp[1]%mo;
fo(i,0,m-1)
{
fo(x,0,n-1) fo(y,0,n-1) ua.a[x][y]=ac.a[x][y]*wp[i]%mo;
fo(x,0,n-1) ua.a[x][x]++;
ua=kst(ua,r);
a[i]=ua.a[st-1][ed-1]*wp[(LL)i*(LL)(i-1)/2%m]%mo;
}
fo(i,0,2*m-2) b[2*m-i]=wp[(m-(LL)i*(LL)(i-1)/2%m)%m];
mul(a,b);
LL ns=ksm(m,mo-2);
fo(i,0,m-1) printf("%lld\n",wp[(LL)i*(LL)(i-1)/2%m]*ns%mo*a[2*m-i]%mo);
}

Day 2 T3 序列

题解:可以发现,最优策略是将原序列分成若干段,每一段取它们的均值,并且均值应该递增。

进一步的,记S[i]为a的1~i前缀和,我们将点\((i,S[i])\)放到平面上,求一个下凸壳,相邻点的连线斜率就是这一段取的均值。用单调栈来做,就可以做到\(O(nm)\)了

考虑优化,修改一个位置相当于后缀的所有点整体下移/上移,我们预处理出前缀凸壳和后缀凸壳(单调栈构成树形结构,总的个数是\(O(n)\)的),现在相当于做一个凸壳合并,我们在前缀凸壳上二分,再相应的在后缀凸壳上二分找到最左的一个连接后满足下凸性的点,判断前缀凸壳此时是否也是凸的相应修改二分区间即可。(二分实际上可以用树上倍增来实现)

时间复杂度\(O(m\log ^2n)\)

实现上细节比较多

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define LL long long
#define LT unsigned long long
#define mo 998244353
using namespace std;
LL a[N],ny[N],s2[N],sm[N],sr[N],ans[N];
LT s[N];
int n,m,st[N],top,f[N][19],ask[N][2],qs[N];
bool pd(int x,int y,int z)
{
return (s[y]-s[x])*(LT)(z-y)>(s[z]-s[y])*(LT)(y-x);
}
bool pd2(int x,int y,int z,LL xd)
{
return (s[y]-s[x]+xd)*(LT)(z-y)>(s[z]-s[y])*(LT)(y-x);
}
bool pd3(int x,int y,int z,LL xd)
{
return (s[y]-s[x])*(LT)(z-y)>(s[z]-s[y]+xd)*(LT)(y-x);
}
LL calc(LL v,int x,int y)
{
if(x>=y) return 0;
return ((s2[y]-s2[x]+mo)%mo+v*v%mo*(LL)(y-x)%mo-(LL)2*((s[y]-s[x])%mo)%mo*v%mo+mo+mo)%mo;
}
LL calc2(LL v,int x,int y,int k,LL xd)
{
if(x>=y) return 0;
LL u=(s2[y]-s2[x]-a[k]*a[k]%mo+(a[k]+xd)*(a[k]+xd)%mo+mo+mo)%mo;
return (u+v*v%mo*(LL)(y-x)%mo-((LL)2*((LL)((s[y]-s[x])%mo)+xd)%mo*v%mo+mo)%mo+mo)%mo;
}
bool pd1(int x,int y,LL xd,int k)
{
int l=1,r=top,mid;
while(l+1<r)
{
mid=(l+r)>>1;
if(pd2(y,st[mid],st[mid-1],xd)) r=mid;
else l=mid;
}
if(r<=l||!pd2(y,st[r],st[r-1],xd)) mid=r;
else mid=l;
return (!pd3(x,y,st[mid],xd));
}
int wz(int y,LL xd,int k)
{
int l=1,r=top,mid;
while(l+1<r)
{
mid=(l+r)>>1;
if(pd2(y,st[mid],st[mid-1],xd)) r=mid;
else l=mid;
}
if(r<=l||!pd2(y,st[r],st[r-1],xd)) mid=r;
else mid=l;
return st[mid];
}
LL query(int k,LL xd)
{
int p=f[k-1][0],q=k-1;
for(int j=18;q>0&&!pd1(p,q,xd,k);)
{
while(j&&pd1(f[p][j],f[q][j],xd,k)) j--;
p=f[p][j],q=f[q][j];
}
int w=wz(q,xd,k);LL v=(s[w]-s[q]+xd)%mo*ny[w-q]%mo;
return (calc(v,q,k-1)+calc2(v,k-1,w,k,xd)+sm[q]+sr[w])%mo;
}
bool cmp(int x,int y)
{
return ask[x][0]<ask[y][0];
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
cin>>n>>m;
st[++top]=0;
ny[1]=1;
fo(i,2,n) ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;
fo(i,1,n) scanf("%lld",&a[i]),s[i]=s[i-1]+a[i],s2[i]=(s2[i-1]+a[i]*a[i]%mo)%mo;
st[top=1]=0;
fo(i,1,n)
{
while(top>1&&pd(st[top-1],st[top],i)) top--;
st[++top]=i;
f[i][0]=st[top-1];
sm[i]=(sm[f[i][0]]+calc((s[i]-s[f[i][0]])%mo*ny[i-f[i][0]]%mo,f[i][0],i))%mo;
}
fo(j,1,18) fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
printf("%lld\n",sm[n]);
fo(i,1,m)
{
scanf("%d%d",&ask[i][0],&ask[i][1]);
qs[i]=i;
}
sort(qs+1,qs+m+1,cmp);
memset(st,0,sizeof(st)),top=0;
int j=m;
fod(i,n,1)
{
while(top>1&&pd(i,st[top],st[top-1])) top--;
st[++top]=i;
sr[i]=sr[st[top-1]];
if(top>1) sr[i]=(sr[i]+calc((s[st[top-1]]-s[i])%mo*ny[st[top-1]-i]%mo,i,st[top-1]))%mo;
while(j>0&&ask[qs[j]][0]==i)
{
ans[qs[j]]=query(i,ask[qs[j]][1]-a[i]);
j--;
}
}
fo(i,1,m) printf("%lld\n",ans[i]);
}