zoukankan      html  css  js  c++  java
  • 【题解】GXOI/GZOI2019 机房游记

    【题解】GXOI/GZOI2019 机房游记

    教练不知道从哪里嫖来的数据(或许是洛谷或者(Loj)?),只说是让我们做套模拟题,后来发现是今年 ( ext{GX,GZ}) 两省联考的考试题。

    由于时间关系,只考 (4) 个小时。本来就不会做,还不给狗时间骗分,这是要爆蛋的节奏啊。

    时间:2019-1-9 (机房 day1)

    考前几分钟打了一局PvZ。

    (T1) 貌似写的是正解,(T2) 直接放弃了,如果再多一个小时或许能骗一点分?(T3) 有一个部分用了我最爱的 ( ext{CDQ}),而且刚好就是我前几天出的那道题的类型,愉快地码了接近两个小时,希望不要写挂吧。

    期望得分:(100+0+100)

    实际得分:(100+0+30)

    等等,(T3) 那些在 (caiji) 评测机 (Cena)(WA) 掉的数据在本地运行可以过,于是又换了台机子重测。

    真·实际得分(100+0+100)

    时间:2019-1-9 (机房 day2)

    (T1) 研究了 (2) 个小时后推出式子,十几分钟码出来开拍。

    (T2) 直接暴力,(T3) 数据结构题,敲了一个多小时后感觉 (50pt) 稳了,又回去看 (T2),发现可以用 (SPFA) 跑多源次短路骗分,(10min) 敲好开溜。

    期望得分:(100+(40 sim 100)+50)

    实际得分:(100+40+50)

    今天把PvZ通关了,开始硬刚95版和beta版。

    【题解】

    【Day1 T1】

    上来先开 ( ext{T1}) 码暴力。一开始把 (or) 看成了 (xor),想着直接上矩阵前缀和,而且居然还过了第一个样例,对着样例 (2) 手玩了好久才发现是应该是 (or)

    我太菜了

    回想起以前做过的一道题:( ext{bob [COCI2015]}),如果所有数字都是 (0) 或者 (1) 的话,就可以直接上单调栈扫过去,很明显这道题可以尝试一些转换。

    由于 (and)(or) 运算对于二进制数字的每一位都是独立的,可以把所有数都拆成 (31)(0)(1),构造出 (31) 个新的矩阵。

    对于每一个矩阵的求解:注意到 (and) 实际上就是统计有多少个全为 (1) 的子矩阵,而 (or) 则是总的子矩阵个数 (left(frac{n(n+1)}{2} ight)^2) 减去全为 (0) 的子矩阵个数。直接单调栈一遍扫过去。

    时间复杂度:(O(n^2loginf))

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1000+3,P=1e9+7;
    int n,t,tot,Ans1,Ans2,Q[N],W[N],a[N][N],A[N][N],U[N][N],LU[N][N];
    inline void in(Re &x){
        Re fu=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=fu?-x:x;
    }
    inline void sakura(){
        for(Re j=1;j<=n;++j)
            for(Re i=1;i<=n;++i)
                if(a[i-1][j]!=a[i][j])U[i][j]=1;
                else U[i][j]=U[i-1][j]+1;
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=n;++j)
                if(a[i][j]!=a[i][j-1])t=0,Q[++t]=j,W[t]=1,LU[i][j]=U[i][j];
                else{
                    Re len=1;LU[i][j]=U[i][j];
                    while(t&&U[i][Q[t]]>U[i][j])(LU[i][j]+=(LL)W[t]*U[i][j]%P)%=P,len+=W[t--];
                    if(t)(LU[i][j]+=LU[i][Q[t]])%=P;
                    Q[++t]=j,W[t]=len;
                }
    }
    int main(){
    //  freopen("andorsum.in","r",stdin);
    //  freopen("andorsum.out","w",stdout);
        in(n),tot=(LL)n*n*(n+1)*(n+1)/4%P;
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=n;++j)
                in(A[i][j]);
        memset(a,-1,sizeof(a));
        for(Re k=30;k>=0;--k){
            Re flag=0,p=(1<<k)%P;
            for(Re i=1;i<=n;++i)
                for(Re j=1;j<=n;++j)
                    flag|=(a[i][j]=((A[i][j]>>k)&1));
            if(!flag)continue;
            Re tmp1=0,tmp2=0;
            sakura();
            for(Re i=1;i<=n;++i)
                for(Re j=1;j<=n;++j)
                    if(a[i][j])(tmp1+=LU[i][j])%=P;
                    else (tmp2+=LU[i][j])%=P;
            (Ans1+=(LL)tmp1*p%P)%=P,(Ans2+=(tot-tmp2+P)%P*1ll*p%P)%=P;
        }
        printf("%d %d
    ",Ans1,Ans2);
        fclose(stdin);
        fclose(stdout);
    }
    

    【Day1 T2】

    神奇的 (dp),不会,先咕着(实际上永远都不会补上了)。

    【Day1 T3】

    一道大膜您。

    ((1).) 首先是求交点。

    (A[i]) 为飞机 (i) 需要到达的点的排名。

    求交点的本质就是在求:对于所有满足(A[i]<A[j])(写法上不同可能是 (A[i]>A[j]))的 (i,j) ((i<j)),直线 (i) 必定与直线 (j) 相交。“统计对于所有满足 (XXX) 条件的 (i,j) ((i<j))”,这不就是个 ( ext{CDQ}) 的板子操作吗?由于交点数不大于 (5e5),可以直接在递归中暴力枚举求交,时间复杂度为:(O(nlogn+) ( ext{交点数}) ())

    ((2).) 计算被观测到的交点的数量。

    先把所有的点都顺时针旋转 (45^circ),然后做二维数点,可以用离线排序+扫描线+树状数组解决。

    (注意 是否被观测 与 选择哪种特技表演 其实是相互独立的,两者可以分开处理)

    ((3).) 分别计算两种特技表演的次数。

    每交换一次就相当于是把某个 (A[i],A[j]) 的值进行了交换,而最后的 (A) 序列一定是排好序的,是不是和求交点很像?模拟一下会发现:在所有的交点处都进行一次交换,最后的达到高度必定满足排名要求。因此当 (a>b) 时最大得分一定为 (a) (*) ( ext{交点个数})

    下面考虑 (a<b) 的情况:用尽量少的交换次数满足到达高度的排名。将所有的 (i)(A[i]) 连边,最后一定会形成若干个环,每个大小为 (x) 的环都可以用 (x-1) 次交换(环中的点相互一共有 (x-1) 个交点)将其归位,而对于不同环中的点的交点则不用管,答案为:(n-) ( ext{环的个数}) 。可以发现这样算出来即为最少需要的交换次数。

    时间复杂度: (O(nlogn))

    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define LD double
    #define Re register int
    #define Vector Point
    using namespace std;
    const int N=1e5+3,M=5e5+3;
    const LD eps=1e-8,Pi=acos(-1);
    int n,a,b,c,K,st,ed,cnt,H1[N],H2[N];LL Ans1,Ans2;
    inline int dcmp(LD a){return a<-eps?-1:(a>eps?1:0);}
    inline LD Abs(LD a){return dcmp(a)*a;}
    inline void in(Re &x){
        Re fu=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=fu?-x:x;
    }
    struct Point{
        LD x,y;Point(LD X=0,LD Y=0){x=X,y=Y;}
        inline void in(){scanf("%lf%lf",&x,&y);}
        inline void out(){printf("%.2lf %.2lf
    ",x,y);}
    }CR[M];
    struct Line{Point a,b;}Li[N];
    inline LD Dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;}//【点积】
    inline LD Cro(Vector a,Vector b){return a.x*b.y-a.y*b.x;}//【叉积】
    inline LD Len(Vector a){return sqrt(Dot(a,a));}//【模长】
    inline Vector operator+(Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
    inline Vector operator-(Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
    inline Vector operator*(Vector a,LD b){return Vector(a.x*b,a.y*b);}
    inline bool operator==(Point a,Point b){return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y);}//两点坐标重合则相等
    inline Point turn_P(Point a,LD theta){//【点A向量A顺时针旋转theta(弧度)】
        LD x=a.x*cos(theta)+a.y*sin(theta);
        LD y=-a.x*sin(theta)+a.y*cos(theta);
        return Point(x,y);
    }
    inline Point cross_LL(Line v1,Line v2){//【两直线AB,CD的交点】
        Point a=v1.a,b=v1.b,c=v2.a,d=v2.b;
        Vector x=b-a,y=d-c,z=a-c;
        return a+x*(Cro(y,z)/Cro(x,y));//点A加上向量AF
    }
    struct Sakura1{
        int A[N],B[N];
        struct QWQ{int i,A;QWQ(int I=0,int a=0){i=I,A=a;}}A_[N],A__[N];
        inline void merge(QWQ *P,Re p1,Re t1,Re p2,Re t2){//归并
            Re t=p1-1;
            while((p1<=t1||p2<=t2))
                if((p1<=t1&&A_[p1].A>A_[p2].A)||p2>t2)P[++t]=A_[p1++];//注意判断是否大于t1,t2
                else P[++t]=A_[p2++];
        }
        inline void CDQ(Re L,Re R){
            if(L==R)return;
            Re mid=L+R>>1,p1=mid,p2=R+1;
            CDQ(L,mid),CDQ(mid+1,R);
            while(p1>=L){
                while(p2>mid+1&&A_[p1].A>A_[p2-1].A)--p2;
                for(Re k=p2;k<=R;++k)CR[++cnt]=cross_LL(Li[A_[p1].i],Li[A_[k].i]);//这里的暴力只会枚举[交点个数]次
                --p1;
            }
            merge(A__,L,mid,mid+1,R);
            for(Re i=L;i<=R;++i)A_[i]=A__[i];
        }
        inline void sakura(){
            for(Re i=1;i<=n;++i)B[i]=H2[i];
            sort(B+1,B+n+1);
            for(Re i=1;i<=n;++i)A[i]=lower_bound(B+1,B+n+1,H2[i])-B;
            for(Re i=1;i<=n;++i)A_[i]=QWQ(i,A[i]);
            CDQ(1,n);
        }
    }T1;
    struct View{Point O;LD r;}V[N];
    struct Sakura2{
        #define lo(a) (lower_bound(b+1,b+m+1,a)-b)
        LD b[M+(N<<1)];
        struct BIT{
            int n,C[M+(N<<2)];
            inline void add_(Re x,Re v){while(x<=n)C[x]+=v,x+=x&-x;}
            inline void add(Re L,Re R,Re v){add_(L,v),add_(R+1,-v);}
            inline int ask(Re x){Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}
        }TR;
        struct Point_{LD x;int y;inline bool operator<(const Point_ &O)const{return dcmp(x-O.x)<0;}}Q[M];
        struct Line_{LD x;int y1,y2,k;inline bool operator<(const Line_ &O)const{return dcmp(x-O.x)<0;}}L[N<<1];
        int m,t;
        inline void sakura(){
            Re tmp=0;LD sq2=sqrt(2.0);
            for(Re i=1;i<=cnt;++i)CR[i]=turn_P(CR[i],Pi/4.0),b[++m]=CR[i].y;//旋转所有点
            for(Re i=1;i<=K;++i)
                V[i].O=turn_P(V[i].O,Pi/4.0),V[i].r*=sq2,
                b[++m]=V[i].O.y-V[i].r/2.0,b[++m]=V[i].O.y+V[i].r/2.0;//离散化所有需要用到的纵坐标
            sort(b+1,b+m+1),TR.n=m=unique(b+1,b+m+1)-b-1;
            for(Re i=1;i<=cnt;++i)Q[i].x=CR[i].x,Q[i].y=lo(CR[i].y);
            for(Re i=1;i<=K;++i)
                L[++t]=(Line_){V[i].O.x-V[i].r/2.0,lo(V[i].O.y-V[i].r/2.0),lo(V[i].O.y+V[i].r/2.0),1},
                L[++t]=(Line_){V[i].O.x+V[i].r/2.0+eps*2.0,lo(V[i].O.y-V[i].r/2.0),lo(V[i].O.y+V[i].r/2.0),-1};//注意在边界上也算,所以要把右边界向右移一点
            sort(Q+1,Q+cnt+1),sort(L+1,L+t+1);
            Re now=0;
            for(Re i=1;i<=cnt;++i){
                while(now<t&&dcmp(L[now+1].x-Q[i].x)<=0)++now,TR.add(L[now].y1,L[now].y2,L[now].k);
                if(TR.ask(Q[i].y))++tmp;//如果被包含
            }
            Ans1+=(LL)c*tmp,Ans2+=(LL)c*tmp;
        }
    }T2;
    struct Sakura3{
        int tmp,fa[N];
        inline int find(Re x){return fa[x]==x?x:fa[x]=find(fa[x]);}
        inline void merge(Re x,Re y){
            if((x=find(x))!=(y=find(y)))fa[x]=y;
            else --tmp;//出现了一个环
        }
        inline void sakura(){
            tmp=n;
            for(Re i=1;i<=n;++i)fa[i]=i;
            for(Re i=1;i<=n;++i)merge(i,T1.A[i]);
            Ans1+=tmp*a+(cnt-tmp)*b,Ans2+=cnt*a;
        }
    }T3;
    int main(){
    //  freopen("aerobatics.in","r",stdin);
    //  freopen("aerobatics.out","w",stdout);
        in(n),in(a),in(b),in(c),in(st),in(ed);
        for(Re i=1;i<=n;++i)in(H1[i]),Li[i].a=Point(st,H1[i]);
        for(Re i=1;i<=n;++i)in(H2[i]),Li[i].b=Point(ed,H2[i]);
        in(K);for(Re i=1;i<=K;++i)V[i].O.in(),scanf("%lf",&V[i].r);
        T1.sakura();//获取n条直线的所有交点CR
        T2.sakura();//获取被观测到的交点数量
        T3.sakura();//获取两种特技的表演次数
        if(a<b)swap(Ans1,Ans2);
        printf("%lld %lld
    ",Ans1,Ans2);
        fclose(stdin);
        fclose(stdout);
    } 
    
    

    【Day2 T1】

    很明显的矩阵优化 (dp) 转移,问题是 (dp) 方程要如何求得。

    (f[n]) 表示放了 (n) 个正常方块的方案数,很明显是个斐波那契数列:(f[n]=f[n-1]+f[n-2]),其中 (f[n-1]) 的含义是新加了一块竖着的方块,(f[n-2]) 是新加了两块横着的方块。

    (dp[n]) 表示放了 (n) 个方块的答案,对于新增的正常方块一样有 (dp[n]=dp[n-1]+dp[n-2]),现在考虑特殊方块的放置。

    注意到一个性质:两个特殊方块之间的所有正常方块形态只有一种,也就是说一旦确立了特殊方块的位置 (l,r) 后,(l)(r) 这一段中间的方案数为 (1),左边的应为 (f[l-1])

    在转移 (dp[n]) 的时候,如果选择在 (n) 的最右边放一个特殊方块,那么左边的特殊方块可以在 (1,2,3...i-2) 这些位置,方案数为 (f[0]+f[1]...f[n-3]),另外,特殊方块可以上下颠倒位置,所以 (dp[n]=dp[n-1]+dp[n-2]+2sum_{i=1}^{n-3}f(i)),又因为 (sum_{i=1}^{n}Fib(i)=Fib(n+2)),所以 (dp[n]=dp[n-1]+dp[n-2]+2*f(n-1)-2)

    最后随便设计一个矩阵即可。

    时间复杂度:(O(k^3logn)),其中 (k=5) 为矩阵大小。

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1e6+3,P=1e9+7;
    int n,T;
    inline void in(Re &x){
        Re fu=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=fu?-x:x;
    }
    struct Matrix{
        int a[6][6];
        Matrix(){memset(a,0,sizeof(a));}
        inline Matrix operator*(const Matrix &O)const{
            Matrix ans;
            for(Re i=1;i<=5;++i)
                for(Re j=1;j<=5;++j)
                    for(Re k=1;k<=5;++k)
                        (ans.a[i][j]+=(LL)a[i][k]*O.a[k][j]%P)%=P;
            return ans;
        }
        inline Matrix operator*=(Matrix O){return *this=*this*O;}
    }A,B;
    inline Matrix mi(Matrix x,Re k){
        Matrix s=x;--k;
        while(k){
            if(k&1)s*=x;
            x*=x,k>>=1;
        }
        return s;
    }
    int main(){
        //freopen("obsession.in","r",stdin);
        //freopen("obsession.out","w",stdout);
        A.a[1][1]=0,A.a[1][2]=0,A.a[1][3]=2,A.a[1][4]=1,A.a[1][5]=-2+P;
        B.a[1][1]=B.a[1][2]=B.a[2][1]=B.a[3][3]=B.a[3][4]=B.a[4][3]=B.a[5][1]=B.a[5][5]=1,B.a[3][1]=2;
        in(T);
        while(T--)in(n),printf("%d
    ",n<3?0:(A*mi(B,n-2)).a[1][1]);
        fclose(stdin);
        fclose(stdout);
    }
    

    【Day2 T2】

    考虑染色法多源最短路。

    分别在原图和反图上对关键点跑一遍多源最短路并染上色,枚举原图中的所有边,如果两端点颜色不同,则说明该边一定为某两个关键点之间的路径上,且对于该边来说,它所连的两个关键点一定是最近的,所以不断取最小值即为答案。

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    #define LL long long
    #define Re register int
    using namespace std;
    const LL N=1e5+3,M=5e5+3,inf=1e18;
    int n,m,x,y,z,K,T,X[M],Y[M],Z[M],A[N];LL Ans;
    inline void in(Re &x){
        Re fu=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=fu?-x:x;
    }
    struct Sakura{
        struct QWQ{int x;LL w;QWQ(int X=0,LL W=0){x=X,w=W;}inline bool operator<(const QWQ &O)const{return w>O.w;};};
        int o,head[N],pan[N],col[N];LL dis[N];
        struct QAQ{int w,to,next;}a[M];priority_queue<QWQ>Q;
        inline void add(Re x,Re y,Re z){if(x==y)return;a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
        inline void dijkstra(){
            for(Re i=1;i<=n;++i)dis[i]=inf,pan[i]=col[i]=0;
            for(Re i=1;i<=K;++i)col[A[i]]=A[i],Q.push(QWQ(A[i],dis[A[i]]=0));
            while(!Q.empty()){
                Re x=Q.top().x;Q.pop();
                if(pan[x])continue;
                pan[x]=1;
                for(Re i=head[x],to;i;i=a[i].next)
                    if(dis[to=a[i].to]>dis[x]+a[i].w)
                        col[to]=col[x],Q.push(QWQ(to,dis[to]=dis[x]+a[i].w));
            }
        }
    }T1,T2;
    inline void CL(){
        for(Re i=1;i<=n;++i)T1.head[i]=T2.head[i]=0;T1.o=T2.o=0;
    }
    int main(){
        //freopen("tourist.in","r",stdin);
        //freopen("tourist.out","w",stdout);
        in(T);
        while(T--){
            in(n),in(m),in(K),Ans=inf;
            for(Re i=1;i<=m;++i)in(x),in(y),in(z),T1.add(X[i]=x,Y[i]=y,Z[i]=z),T2.add(y,x,z);
            for(Re i=1;i<=K;++i)in(A[i]);
            T1.dijkstra(),T2.dijkstra();
            for(Re i=1;i<=m;++i){
                x=X[i],y=Y[i],z=Z[i];
                if(T1.col[x]&&T2.col[y]&&T1.col[x]!=T2.col[y])
                    Ans=min(Ans,T1.dis[x]+T2.dis[y]+z);
            }
            printf("%lld
    ",Ans),CL();
        }
        fclose(stdin);
        fclose(stdout);
    }
    

    【Day2 T3】

    考虑先将询问离线,按 (x) 的大小排序。

    首先看 (k=1) 的情况,(dep[x]^k) 相当于就是统计根到 (x) 的距离加 (1),即 (dep[x]=1+1+...+1) (() 一共 (dep[x])(1) ())。当有多个 (dep[x]^k) 需要统计时,对于每个 (x),把从根到 (x) 的这条路径上的权值全部加一,然后查询 (y) 时就直接查询从根到 (y) 这条路径上的权值和。上述操作可以用一个简化版的树剖实现。

    对于 (k!=1) 的情况:考虑将 (dep[x]^k) 分为 (dep[x]) 段相加,然后用类似上面的操作实现。如何分?设 (f(x)=dep[x]^k),则有 (f(x)=((dep[x]!-!1)^k)+(dep[x]^k-(dep[x]!-!1)^k)) (=f(x!-!1)+(dep[x]^k-(dep[x]!-!1)^k)),所以 (f(x)=(dep[1]^k-(dep[1]!-!1)^k) + (dep[2]^k-(dep[2]!-!1)^k)...(dep[x]-(dep[x]-1)^k)),是不是和上面的很像?只是这里每次就不是加 (1) 了,而是对于每一个在根到 (x) 路径上的点 (p) 都加上一个 ((dep[p]^k-(dep[p]!-!1)^k))

    但这样做复杂度会高至 (n^2),考虑用万能的线段树来维护。

    由于每次加的值 ((dep[p]^k-(dep[p]!-!1)^k)) 对于 (p) 来说都是不变的,那么对于一个等待着加值的区间也是不变,所以直接预处理出线段树上所有区间的加值和,用懒标记表示该区间待加的次数,(pushdown) 的时候算次数乘以区间加值和即可。

    时间复杂度:(O(nlog^2n))

    【Code】

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=5e4+3,P=998244353;
    int o,n,x,y,T,K,flag,A[N],AA[N],fa[N],deep[N],head[N],Ans[N];
    struct QAQ{int to,next;}a[N];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    struct Query{int x,y,id;}Q[N];
    inline bool operator<(Query A,Query B){return A.x<B.x;}
    inline void in(Re &x){
        Re fu=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=fu?-x:x;
    }
    inline int mi(Re x,Re k){
        Re s=1;
        while(k){
            if(k&1)s=(LL)s*x%P;
            x=(LL)x*x%P,k>>=1;
        }
        return s;
    }
    struct Killed_Tree{
        int id_o,id[N],idx[N],top[N],son[N],size[N];
        struct Segment_Tree{
            #define pl (p<<1)
            #define pr (p<<1|1)
            #define mid (L+R>>1)
            struct QAQ{int S,l,r,add,key;}tr[N<<2];
            inline void updata(Re p,Re v){
                (tr[p].S+=(LL)tr[p].key*v%P)%=P,(tr[p].add+=v)%=P;
            }
            inline void pushdown(Re p){
                Re v=tr[p].add;
                if(v)updata(pl,v),updata(pr,v),tr[p].add=0;
            }
            inline void build(Re p,Re L,Re R){
                tr[p].l=L,tr[p].r=R;
                if(L==R){tr[p].key=AA[L];return;}
                build(pl,L,mid),build(pr,mid+1,R);
                tr[p].key=(tr[pl].key+tr[pr].key)%P;
            }
            inline void change(Re p,Re l,Re r,Re v){
                Re L=tr[p].l,R=tr[p].r;
                if(l<=L&&R<=r){updata(p,v);return;}
                pushdown(p);
                if(l<=mid)change(pl,l,r,v);
                if(r>mid)change(pr,l,r,v);
                tr[p].S=(tr[pl].S+tr[pr].S)%P;
            }
            inline int ask(Re p,Re l,Re r){
                Re L=tr[p].l,R=tr[p].r;
                if(l<=L&&R<=r)return tr[p].S;
                Re ans=0;pushdown(p);
                if(l<=mid)(ans+=ask(pl,l,r))%=P;
                if(r>mid)(ans+=ask(pr,l,r))%=P;
                return ans;
            }
        }TR;
        inline void dfs1(Re x,Re Fa){
            size[x]=1,deep[x]=deep[fa[x]=Fa]+1;
            for(Re i=head[x],to;i;i=a[i].next)
                if((to=a[i].to)!=Fa){
                    dfs1(to,x);
                    size[x]+=size[to];
                    if(size[to]>size[son[x]])son[x]=to;
                }
        }
        inline void dfs2(Re x,Re rt){
            AA[id[x]=++id_o]=A[x],idx[id_o]=x,top[x]=rt;
            if(!son[x])return;
            dfs2(son[x],rt);
            for(Re i=head[x],to;i;i=a[i].next)
                if((to=a[i].to)!=fa[x]&&to!=son[x])dfs2(to,to);
        }
        inline void kill(Re rt){
            dfs1(rt,0);
            for(Re i=1;i<=n;++i)A[i]=(mi(deep[i],K)-mi(deep[i]-1,K)+P)%P;
            dfs2(rt,rt),TR.build(1,1,n);
        }
        inline int ask_dis(Re x){//询问x到y的简单路径上权值之和
            int ans=0;
            while(x)(ans+=TR.ask(1,id[top[x]],id[x]))%=P,x=fa[top[x]]; 
            return ans;
        }
        inline void change_dis(Re x,Re v){//x到y的简单路径上权值加上v
            while(x)TR.change(1,id[top[x]],id[x],v),x=fa[top[x]];
        }
    }T1;
    inline void sakura(){
        Re now=0;
        for(Re i=1;i<=T;++i){
            while(now<Q[i].x)++now,T1.change_dis(now,1);
            Ans[Q[i].id]=T1.ask_dis(Q[i].y);
        }
    }
    int main(){
        int size = 8 << 20;
        char *p = (char*)malloc(size) + size;
        __asm__("movl %0, %%esp
    " :: "r"(p));
        //freopen("poetry.in","r",stdin);
        //freopen("poetry.out","w",stdout);
        in(n),in(T),in(K);
        for(Re i=2;i<=n;++i)in(fa[i]),add(fa[i],i);
        T1.kill(1);
        for(Re i=1;i<=T;++i)in(Q[i].x),in(Q[i].y),Q[i].id=i;
        sort(Q+1,Q+T+1);
        sakura();
        for(Re i=1;i<=T;++i)printf("%d
    ",Ans[i]);
        fclose(stdin);
        fclose(stdout);
    }
    
  • 相关阅读:
    Mondriaan's Dream POJ
    H
    Superdoku Kattis
    Corn Fields POJ
    旅行的意义 Gym
    G
    J
    Welcome Party ZOJ
    redis入门到精通系列(三):key的通用操作和redis内部db的通用操作
    redis入门到精通系列(二):redis操作的两个实践案例
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12182966.html
Copyright © 2011-2022 走看看