【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]);
}

猜你喜欢

转载自www.cnblogs.com/BAJimH/p/10690153.html