【HNOI2019】部分题简要题解

题意懒得写了
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]);
}
posted @ 2019-04-11 15:46  BAJim_H  阅读(148)  评论(0编辑  收藏  举报