zoukankan      html  css  js  c++  java
  • 【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
    ",&a[i].x,&a[i].y);
    	ans=0;
    	fo(i,1,n) 
    	{
    		ans+=query(i);
    	}
    	printf("%lld
    ",ans);
    }
    

    Day 1 T3 多边形

    题解:观察样例可以发现,最终状态一定是n-3条边都与n号点相连。
    那么次数最少的操作一定每一次都是要连到n的
    进一步可以发现,某些边的操作一定在某条边之后,先后关系可以构成一个森林。
    操作次数就是森林点数,方案数就是森林的拓扑序种数。
    现在提前给出一些操作,观察这些操作在树上的变化,要么直接删去一个点,要么改变一些父子关系,改变的个数只有常数个,直接计算这些点对答案的影响即可。

    用map来存标号,时间复杂度(O(nlog 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("
    ");
    	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
    ");
    			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("
    ");
    	}
    }
    

    Day 2 T1 校园旅行

    题解:考虑一个暴力DP,(f[x][y])表示x,y是否能通过回文串到达,枚举两边的出边,按照BFS的顺序转移,时间复杂度(O(m^2))
    考虑优化,我们将边分类,要么是连接不同颜色的边,要么是连接相同颜色的边。
    只取出连接相同颜色的边,考虑每一个连通块,如果它是个二分图,那么保留一棵生成树即可(因为匹配配只与奇偶性有关,数量不同可以在一条边反复横跳满足)。如果不是,那么奇偶性可以改变,可以任意匹配,那么保留一棵生成树,再连上一个自环表示奇环的情况。对于连接不同颜色的边的联通块,它显然是二分图,直接保留生成树即可。
    连边采用并查集,时间复杂度(O(malpha(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("
    ");
    	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
    ");
    		else printf("NO
    ");
    	}
    }
    

    Day 2 T2 白兔之舞

    考虑枚举走了i步,容易得出(Ans_t=sumlimits_{i=0}^{L}[(i-t)\%k==0]{Lchoose i}W^i_{x,y})
    根据单位根反演的公式([n|k]={1over n}sumlimits_{i=0}^{n-1}omega_n^{ki})
    这里单位根我们找一个p的原根,然后取其(p-1)/k次作为K次单位根
    代入,交换主体可得
    (Ans_t=sumlimits_{j=0}^{k-1}omega_k^{-jt}sumlimits_{i=0}^{L}{Lchoose i}left(omega_k^j ight)^iW^i_{x,y})
    后面的部分用二项式定理就是(left(omega_k^jW+I ight)^L_{x,y}),枚举j,用矩阵快速幂做
    前面的(omega_k^{-jt}),一个经典的套路就是将(jt)拆成({j+tchoose 2}-{jchoose 2}-{tchoose 2})
    这样剩下的就是一个卷积了,用任意模数NTT/MTT优化即可。
    时间复杂度(O(n^3klog L+klog 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
    ",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(mlog ^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
    ",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
    ",ans[i]);
    }
    
  • 相关阅读:
    解决DataGridView中回车换行的问题
    几条经典的SQL语句
    SQL中把一个表中的数据导出到一个新表中
    [转载]TCP的网络编程中一些典型的问题,以及一些分析和解决方案
    Delphi调用C#web服务参数无法接收的问题
    IIS服务器不支持中文文件名的解决方法
    SQL SERVER2005导入导出工具
    为远程IP服务器取个本地认识的主机名
    Win Form中如何把ENter回车键转换成Tab键
    CheckedListBox 用法
  • 原文地址:https://www.cnblogs.com/BAJimH/p/10690153.html
Copyright © 2011-2022 走看看