zoukankan      html  css  js  c++  java
  • GXOI&GZOI

    T1 与或和

    2s&&512MB

    简明题意:求一个矩阵的所有子序列的 (and)和 和(or)和;

    子矩阵的(and)和就是所有值(and)起来;(or)类似;

    矩阵边长(n<=1000),权值(<=2^{31}-1)

    (&)( |)运算没有逆运算,所以无法算前缀和;但这两种运算中 二进制下的每一位是独立运算的,我们考虑将每个数看成(30)(01)串,一位一位分开算;

    先看与运算,枚举每一位(i),只有当一个子矩阵这一位全是(1)时,这个矩阵会在这一位上对答案产生贡献;

    所以我们就是要找到全(1)子矩阵个数,对答案(ans1)产生个数(*(1<<i))的贡献;接下来就是每行一个单调栈的过程;

    或运算相反,需要求全(0)矩阵,但每次贡献应该减去,初始(ans2)应该是所有子矩阵个数(*((1<<30)-1))

     
     
     

    (Code)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    using namespace std;
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    const int N=1005;
    const int mod=1e9+7;
    int n,a[N][N],h[N],f[N],h1[N];
    int sta[N],top;
    ll tmp;
    ll ans1,ans2;
    int main()
    {
    	freopen("andorsum.in","r",stdin);
    	freopen("andorsum.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    		{
    			a[i][j]=read();
    			ans2=(ans2+i*j)%mod;
    		}
    			
    	ans2=ans2*(((1ll<<31)-1)%mod)%mod;
    	for(int o=0;o<=30;o++)
    	{
    		tmp=0;
    		for(int i=1;i<=n;i++) h[i]=0,f[i]=0,h1[i]=0;
    		for(int i=1;i<=n;i++)
    		{
    			top=0;	
    			for(int j=1;j<=n;j++)
    			{
    				if((a[i][j]>>o)&1) h[j]++;
    				else h[j]=0;	
    				while(h[sta[top]]>=h[j]&&top) top--;
    				f[j]=h[j]*(j-sta[top]);
    				f[j]+=f[sta[top]];
    				tmp+=f[j];
    				sta[++top]=j;
    			}
    		}
    		ans1+=tmp%mod*(1ll<<o)%mod;ans1%=mod;
    		tmp=0;
    		for(int i=1;i<=n;i++)
    		{
    			top=0;	
    			for(int j=1;j<=n;j++)
    			{
    				if((a[i][j]>>o)&1) h1[j]=0;
    				else h1[j]++;	
    				while(h1[sta[top]]>=h1[j]&&top) top--;
    				f[j]=h1[j]*(j-sta[top]);
    				f[j]+=f[sta[top]];
    				tmp+=f[j];
    				sta[++top]=j;
    			}
    		}
    		ans2-=tmp%mod*(1ll<<o)%mod;ans2=(ans2+mod)%mod;
    	}
    	printf("%lld %lld",ans1,ans2);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

     
     
     

    T2 宝牌一大堆

    链接

    先明确刚子永远没有选刻子优秀,就不考虑他了;

    现在只有(3)种情况,国士无双和七对子可以单独模拟,我们只需要求(3*4+2)这种情况了;

    需要进行(DP)

    定义(f[i][j][k][l][m][n])代表,前(i)种牌,选了(j)组面子,(k)组雀头,第(i-2)种用了(l),第 (i-1)种用了(m)张,第(i)种用了(n)张,前(i-1)种牌产生的分数;

     
     

    然后再转移是选一组刻子顺子或雀头;

     
     

    很恶毒的一个(DP)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    #define Vector point
    using namespace std;
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    int T;
    int v[128],num[35],vab[35],c[10][10],val[35],V[35];
    ll ans;
    char st[5];
    inline void reset()
    {
    	for(int i=1;i<=34;i++) num[i]=4,V[i]=1;
    	memset(vab,0,sizeof vab);
    }
    inline ll ksm(int x,int y)
    {
    	ll res=1;
    	ll X=x;
    	while(y){ if(y&1) res=res*X;X*=X;y>>=1;}
    	return res;
    } 
    inline ll gsws()
    {
    	int sum=0,S=0;
    	ll res=1;
    	for(int i=0;i<3;i++)
    	{
    		if(num[1+i*9]==0) return 0;
    		else sum+=num[1+i*9],S+=vab[1+i*9],res=res*c[num[1+i*9]][1];
    		if(num[9+i*9]==0) return 0;
    		else sum+=num[9+i*9],S+=vab[9+i*9],res=res*c[num[9+i*9]][1];
    	}
    	for(int i=28;i<=34;i++)
    		if(num[i]==0) return 0;
    		else sum+=num[i],S+=vab[i],res=res*c[num[i]][1];
    	if(sum<14) return 0;
    	ll tmp=res;
    	res=0;
    	for(int i=0;i<3;i++)
    	{
    		if(num[1+i*9]>=2) res=max(res,tmp/c[num[1+i*9]][1]*c[num[1+i*9]][2]*ksm(2,S+vab[1+9*i]));
    		if(num[9+i*9]>=2) res=max(res,tmp/c[num[9+i*9]][1]*c[num[9+i*9]][2]*ksm(2,S+vab[9+9*i]));
    	}
    	for(int i=28;i<=34;i++)
    		if(num[i]>=2) res=max(res,tmp/c[num[i]][1]*c[num[i]][2]*ksm(2,S+vab[i]));
    	return res*13;
    }
    inline ll get7()
    {
    	int num7=0;
    	for(int i=1;i<=34;i++)
    	{
    		if(num[i]>=2) num7++,val[i]=c[num[i]][2]*(vab[i]?4:1);
    		else val[i]=1;
    	}
    	if(num7<7) return 0;
    	ll res=1;
    	sort(val+1,val+1+34);
    	for(int i=34;i>=28;i--)
    		res*=val[i];
    	return res*7;
    }
    ll f[40][5][2][5][5][5],tmp;
    int main()
    {
    	freopen("doraippai.in","r",stdin);
    	freopen("doraippai.out","w",stdout);
    	c[0][0]=1;c[1][0]=1;c[2][0]=1;c[3][0]=1;c[4][0]=1;c[1][1]=1;c[2][1]=2;c[2][2]=1;c[3][1]=3;c[3][2]=3;c[3][3]=1;c[4][1]=4;c[4][2]=6;c[4][3]=4;c[4][4]=1;
    	v['p']=1;
    	v['s']=2;
    	v['E']=28;
    	v['S']=29;
    	v['W']=30;
    	v['N']=31;
    	v['Z']=32;
    	v['B']=33;
    	v['F']=34;
    	T=read();
    	while(T--)
    	{
    		reset();
    		scanf("%s",st);
    		while(st[0]!='0')
    		{
    			if(st[0]>='1'&&st[0]<='9') 
    			{
    				num[st[0]-'0'+v[st[1]]*9]--;
    			}
    			else num[v[st[0]]]--;
    			scanf("%s",st);
    		}
    		scanf("%s",st);
    		while(st[0]!='0')
    		{
    			if(st[0]>='1'&&st[0]<='9') 
    			{
    				vab[st[0]-'0'+v[st[1]]*9]=1;
    				V[st[0]-'0'+v[st[1]]*9]=2;
    			}
    			else vab[v[st[0]]]=1,V[v[st[0]]]=2;
    			scanf("%s",st);
    		}
    		ans=0;
    		ans=max(ans,gsws());
    		ans=max(ans,get7());
    		memset(f,0,sizeof f);
    		f[1][0][0][0][0][0]=1;
    		for(int i=1;i<=34;i++)
    			for(int j=0;j<=4;j++)
    				for(int k=0;k<=1;k++)
    					for(int l=0;l<=4;l++)
    						for(int m=0;m<=4;m++)
    							for(int n=0;n<=4;n++)
    							{
    								if(!f[i][j][k][l][m][n]) continue;
    								tmp=f[i][j][k][l][m][n];
    								
    								if(k==0&&n+2<=num[i])
    									f[i][j][1][l][m][n+2]=max(f[i][j][1][l][m][n+2],tmp);//雀头 
    									
    								if(j<4&&n+3<=num[i])
    									f[i][j+1][k][l][m][n+3]=max(f[i][j+1][k][l][m][n+3],tmp);//刻子 
    									
    								if(j<4&&i<28&&i%9!=1&&i%9!=2&&n+1<=num[i]&&m+1<=num[i-1]&&l+1<=num[i-2])//顺子 
    									f[i][j+1][k][l+1][m+1][n+1]=max(f[i][j+1][k][l+1][m+1][n+1],tmp/c[num[i-2]][l]*c[num[i-2]][l+1]/c[num[i-1]][m]*c[num[i-1]][m+1]*V[i-2]*V[i-1]);
    								
    								if(i<34) f[i+1][j][k][m][n][0]=max(f[i+1][j][k][m][n][0],tmp*c[num[i]][n]*ksm(V[i],n));
    								if(k==1&&j==4) ans=max(ans,tmp*c[num[i]][n]*ksm(V[i],n));
    							}
    		printf("%lld
    ",ans);
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

     
     
     
     

    T3 特技飞行

    链接

     
     

    问题很复杂,但好像有很多不相干的问题;比如专家加分是不会影响技术得分;

    怎么求专家得分?

    也就是说怎么求被观测到的交点数?

    交点不可能(n^2)求,但交点个数范围是可行的;

    两条线段相交地条件是在起点的顺序与在终点的不一样;也就是一对逆序对;

    就可以在归并排序的同时求交点(找逆序对),这里的时间是(O(nlogn+q))(q)是交点个数,因为求的每一个交点都是不同的;

     
     

    求出所有交点之后,就要求在范围内的个数;但每个范围都是一个正方形旋转(45)度,不方便求解;

    那就把所有点都旋转(45)度,再把每条边扩大根号(2)倍,当然每个点与原点形成的向量也要扩大,(应该也可以都不扩大),然后就是在一堆矩形求被包括的点,这里可以用扫描线;用差分树状数组维护;

     
     

    第二个问题就是技术得分,首先发现一次擦身而过就是制造了一对逆序对,而对向交换没有改变顺序,最后要求顺序不变,所以完全可以全部进行对向交换,根据贪心,如果(A>B)就全部对向交换,否则尽量少对向交换,就是求最多可以擦身而过多少次;

     
     

    我们将一个点与它的目的地点相连

    比如(1)号飞机要去(y)最小的位置,而这个位置是(2)号飞机的目的地,就把(1)(2)连起来;

    这样就会得到很多环,而每一次对向交换可以将环上两个点缩成一个点,最后环都成了一个点就行了,这样每个点至少要对向交换(len-1)次,总共就需要交换(n- 环数)次,剩下的就是最多进行的擦身而过的次数;

     
     

    (Code)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    #define Vector point
    using namespace std;
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    struct point 
    {
    	double x,y;
    	void init(){ scanf("%lf%lf",&x,&y);}
    	point(double X=0,double Y=0):x(X),y(Y){}
    	bool operator <(point w) const
    	{
    		return x==w.x?y<w.y:x<w.x;
    	}
    };
    point operator +(point a,point b){return point(a.x+b.x,a.y+b.y);}
    point operator -(point a,point b){return point(a.x-b.x,a.y-b.y);}
    point operator *(point a,double b){return point(a.x*b,a.y*b);}
    double operator *(point a,point b){return a.x*b.x+a.y*b.y;}
    inline double cross(point a,point b){ return a.x*b.y-a.y*b.x;}
    struct line
    {
    	point s,t;int id;
    };
    inline point lipo(point a,point b,point c,point d)
    {
    	Vector v1=b-a,v2=d-c,v3=c-a;
    	double t=cross(v3,v1)/cross(v1,v2);
    	return c+v2*t;
    }
    const int N=100005,M=500005;
    int n,A,B,C,st,ed,jdnum,ans1,ans2;
    line p[N],b[N];
    point jd[M];
    inline void qsort(int l,int r)
    {
    	if(l==r) return ;
    	int mid=l+r>>1;
    	qsort(l,mid);qsort(mid+1,r);
    	int i=l,j=mid+1,k=l;
    	while(i<=mid&&j<=r)
    	{
    		if(p[i].t.y<p[j].t.y) b[k++]=p[i++];
    		else
    		{
    			b[k++]=p[j++];
    			for(int o=i;o<=mid;o++)
    				jd[++jdnum]=lipo(p[o].s,p[o].t,p[j-1].s,p[j-1].t);
    		}
    	}
    	while(i<=mid) b[k++]=p[i++];
    	while(j<=r) b[k++]=p[j++];
    	for(int o=l;o<=r;o++)
    		p[o]=b[o];
    }
    const double eps=1e-7;
    int tot;
    struct FT
    {
    	int v[M+N+N];
    	void add(int x,int y)
    	{
    		for(;x<=tot;x+=x&-x) v[x]+=y;
    	}
    	int ask(int x)
    	{
    		int res=0;
    		for(;x>0;x-=x&-x) res+=v[x];
    		return res;
    	}
    }ft;
    struct scan
    {
    	int m,numx;
    	double lsh[M+N+N];
    	struct L
    	{
    		double x,y1,y2;int w;
    		bool operator<(L B)const{return x<B.x;}
    	}c[M+N+N];
    	
    	void pre()
    	{
    		for(int i=1;i<=jdnum;i++)
    		{
    			double x=jd[i].x,y=jd[i].y;
    			jd[i].x=x+y;jd[i].y=y-x;
    			lsh[++numx]=jd[i].y;
    		}
    		sort(jd+1,jd+1+jdnum);
    		m=read();
    		for(int i=1;i<=m;i++)
    		{
    			double x,y,r,X,Y;
    			scanf("%lf%lf%lf",&x,&y,&r);
    			X=x+y;Y=y-x;
    			point p1,p2,p3,p4;
    //			r=r*sqrt(2)/2*sqrt(2);
    			int i1=i+i-1,i2=i+i;
    			c[i1]=(L){X-r-eps,Y-r-eps,Y+r+eps,1};
    			c[i2]=(L){X+r+eps,Y-r-eps,Y+r+eps,-1};
    			lsh[++numx]=Y-r-eps;lsh[++numx]=Y+r+eps;
    		}
    		m<<=1;
    		sort(c+1,c+1+m);
    		sort(lsh+1,lsh+1+numx);
    		lsh[0]=-1e10;
    		for(int i=1;i<=numx;i++)
    			if(fabs(lsh[i]-lsh[i-1])>eps)
    				lsh[++tot]=lsh[i];
    		for(int i=1;i<=jdnum;i++)
    			jd[i].y=lower_bound(lsh+1,lsh+tot+1,jd[i].y)-lsh;
    		for(int i=1;i<=m;++i)
    		{
    			c[i].y1=lower_bound(lsh+1,lsh+tot+1,c[i].y1)-lsh;
    			c[i].y2=lower_bound(lsh+1,lsh+tot+1,c[i].y2)-lsh;
    		}
    	}
    	int work()
    	{
    		pre();
    		int res=0;
    		for(int i=1,j=1;i<=jdnum;i++)
    		{
    			while(j<=m&&c[j].x<=jd[i].x)
    			{
    				ft.add(c[j].y1,c[j].w);
    				ft.add(c[j].y2+1,-c[j].w);
    				j++;
    			}
    			if(ft.ask(jd[i].y)) ++res;
    		}
    		return res;
    	}
    }SC;
    int fa[N];
    inline int get(int x)
    {
    	return x==fa[x]?x:fa[x]=get(fa[x]);
    }
    inline void merge(int x,int y)
    {
    	int f1=get(x),f2=get(y);
    	if(f1!=f2)	fa[f1]=f2;
    }
    inline int getcir()
    {
    	for(int i=1;i<=n;i++) fa[i]=i;
    	for(int i=1;i<=n;i++) merge(i,p[i].id);
    	int res=0;
    	for(int i=1;i<=n;i++) if(get(i)==i) res++;
    	return jdnum-n+res;
    }
    int main()
    {
    	freopen("aerobatics.in","r",stdin);
    	freopen("aerobatics.out","w",stdout);
    	n=read();A=read();B=read();C=read();st=read();ed=read();
    	for(int i=1;i<=n;i++) scanf("%lf",&p[i].s.y),p[i].s.x=st;
    	for(int i=1;i<=n;i++) scanf("%lf",&p[i].t.y),p[i].t.x=ed,p[i].id=i;
    	qsort(1,n);
    	int numc=SC.work();
    	ans1=numc*C+jdnum*A;ans2=ans1;
    	ans2+=(B-A)*getcir();
    	if(ans1>ans2) swap(ans1,ans2);
    	printf("%d %d",ans1,ans2);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

     
     
     

    T4 逼死强迫症

    链接

     
     

    先考虑全是(1*2)的砖,(g[i])代表长度为(i)的路的铺砖方法;经过画图发现(g[i]=g[i-2]+g[i-1]),就是在(i-2)基础上加两块横着的,或在(i-1)的基础上加一块竖着的;

    (g)就是一个斐波那契数列;

    再考虑加上(1*1)的;

    如果两块砖都没在边上的话,一定是中间一个子问题,两边用横砖或竖砖补齐,为了不重复,用上一段的办法应该产生贡献(f[i-1]+f[i-2])

    另外如果有一块砖在最边上(比如在最左边),那么两块砖之间只有一种排列方式,而在右边的那块砖的右边可以随意摆放,这里就可以利用(g)了;

    枚举另一块砖所在的地方,转化一下就可以计算(g)的前缀和记为(h)

    [f[i]=f[i-1]+f[i-2]+2*h[i-3] ]

    二倍是由于左右都可;

    又有斐波那契数列的性质(h[i]=g[i+2]-1)

    所以

    [f[i]=f[i-1]+f[i-2]+2*g[i-1]-2 ]

    然后就是矩阵快速幂;

    (Code)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    #define Vector point
    using namespace std;
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    const int mod=1e9+7;
    int T,n;
    int ans;
    struct Matrix
    {
    	int v[5][5];
    	Matrix(){ memset(v,0,sizeof v);}
    	Matrix operator *(Matrix b)
    	{
    		Matrix c;
    		for(int i=0;i<5;i++)
    			for(int j=0;j<5;j++)
    				for(int k=0;k<5;k++)
    					c.v[i][j]=(c.v[i][j]+(ll)v[i][k]*b.v[k][j]%mod)%mod;
    		return c;
    	}
    }a,b,c,d;
    inline Matrix ksm(Matrix x,int y)
    {
    	Matrix res=x;y--;
    	while(y)
    	{
    		if(y&1) res=res*x;
    		x=x*x;
    		y>>=1;
    	}
    	return res;
    }
    int main()
    {
    	freopen("obsession.in","r",stdin);
    	freopen("obsession.out","w",stdout);
    	
    	T=read();
    	a.v[0][0]=a.v[0][1]=a.v[1][0]=a.v[2][2]=a.v[2][3]=a.v[3][2]=a.v[4][4]=1;a.v[2][0]=2;a.v[4][0]=mod-1;
    	b.v[0][0]=2;b.v[0][2]=3;b.v[0][3]=2;b.v[0][4]=2;
    	while(T--)
    	{
    		n=read();
    		if(n<3) printf("0
    ");
    		else
    		{
    			if(n==3) printf("2
    ");
    			else
    			{
    				n-=3;
    				c=a;d=b;
    				printf("%d
    ",(d*ksm(c,n)).v[0][0]);
    			}
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    }
    

    T5 旅行者

    链接

    题解的做法:将所有关键点分成两部分,一部分与一个源点连边权为(0)的边,另一部分与汇点连边权为(0)的边,跑一次源点到汇点的最短路,得到的就是两个部分的最短路,但同一部分内的最短路也可能是答案,我们考虑一个划分方案:按二进制下的每一位分,相同的在同一部分,因为答案点对一定是不一样的,就一定有一个二进制位不一样,这样不失一般性;时间是(O(Tnlog^2n))

    好像会(T)

    求关键点多源最短路有一个方法,跑一次最短路算法计算出每个点,离它最近的关键点((col))和距离((d));

    再枚举每一条边((u,v)),如果(col[u]!=col[v])就用(d[u]+d[v]+V(u,v))更新答案;

    这也不失一般性;因为最短的那条路径一定存在上述的一条边;

    但这道题是有向图;我们必须分别求出去和会的最近距离,才是一条满足的路径;所以必须建反图再跑一次;时间是(O(Tnlogn))

    (Code)

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    #define Vector point
    using namespace std;
    inline int read()
    {
    	int x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    const int N=1e6+20;
    int T,n,m,k;
    int ci[N],isc[N];
    int v[N];
    ll ans;
    struct graph
    {
    	int head[N],cnt;
    	ll d[N];
    	int v[N],col[N];
    	struct skr
    	{
    		int to,nxt,v;
    	}a[N];
    	inline void add(int x,int y,int z)
    	{
    		a[++cnt].to=y;a[cnt].nxt=head[x];a[cnt].v=z;head[x]=cnt;
    	}
    	void clear(){cnt=0;	memset(head,0,sizeof head);}
    	priority_queue<pair<ll,int> >q;
    	inline void dij()
    	{
    		for(int i=1;i<=n;i++) d[i]=1e18;
    		for(int i=1;i<=n;i++) v[i]=0;
    		while(q.size()) q.pop();
    		for(int i=1;i<=k;i++)
    			d[ci[i]]=0,col[ci[i]]=ci[i],q.push(mp(0,ci[i]));
    		while(q.size())
    		{
    			int x=q.top().second;q.pop();
    			if(v[x]) continue;
    			v[x]=1;
    			for(int i=head[x];i;i=a[i].nxt)
    			{
    				int to=a[i].to;
    				if(d[to]>d[x]+a[i].v)
    				{
    					d[to]=d[x]+a[i].v;
    					col[to]=col[x];
    					q.push(mp(-d[to],to));
    				}
    			}
    		}
    	}
    }A,B;
    
    int main()
    {
    	freopen("tourist.in","r",stdin);
    	freopen("tourist.out","w",stdout);
    	T=read();
    	while(T--)
    	{
    		n=read();m=read();k=read();
    		A.clear();B.clear();
    		for(int i=1;i<=m;i++)
    		{
    			int x=read(),y=read(),z=read();
    			if(x==y) continue;
    			A.add(x,y,z);
    			B.add(y,x,z);
    		}
    		for(int i=1;i<=k;i++)
    			ci[i]=read();
    		A.dij();B.dij();
    		ans=1e18;
    		for(int x=1;x<=n;x++)
    			for(int i=A.head[x];i;i=A.a[i].nxt)
    			{
    				int y=A.a[i].to;
    				if(A.col[x]!=B.col[y]) ans=min(ans,A.d[x]+B.d[y]+A.a[i].v);
    			}
    		printf("%lld
    ",ans);
    	}
    	fclose(stdin);
    	fclose(stdout);
    }
    

     
     
     

    T6 旧词

    链接

    先考虑(k=1)的情况;(lca)的深度就是根节点到它的距离,我们把贡献分摊到每个节点上;

    先把询问按(x)排序,考虑一个一个地加,加入一个点就把这个点到根节点的路径的点权都加1;

    查询,就询问(y)到根节点的权值和;可以用(LCT)维护;

    (k!=1) 我们还想让一个点到根节点的路径和等于贡献(深度的(k)次方),那就差分一下,每次加值就加上((d)^k-(d-1)k)这也可以用(LCT)维护;

    (Code)

     
     

    #include<bits/stdc++.h>
    #define ll long long 
    #define mp make_pair
    #define Vector point
    using namespace std;
    inline ll read()
    {
    	ll x=0,fl=1;char st=getchar();
    	while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    	while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    	return x*fl;
    }
    const ll N=50005,mod=998244353;
    ll n,m,k;
    ll d[N],b[N];
    struct LCT
    {
    	#define ls ch[x][0]
    	#define rs ch[x][1]
    	ll fa[N],ch[N][2];
    	ll tag[N],v[N],s[N],sz[N];ll sta[N];
    	bool nort(ll x){ return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    	void upd(ll x){ s[x]=(s[ls]+s[rs]+(ll)v[x]*b[x])%mod;sz[x]=sz[ls]+sz[rs]+b[x];}
    	void rev(ll x,ll y){ s[x]=(s[x]+sz[x]*y%mod)%mod;tag[x]=(tag[x]+y)%mod;v[x]=(v[x]+y)%mod;}
    	void pd(ll x)
    	{
    		if(tag[x])
    		{
    			if(ls) rev(ls,tag[x]);
    			if(rs) rev(rs,tag[x]);
    			tag[x]=0;
    		}
    	}
    	void rotate(ll x)
    	{
    		ll y=fa[x],ys=(ch[y][1]==x);
    		ll R=fa[y];
    		ll B=ch[x][ys^1];
    		if(nort(y)) ch[R][ch[R][1]==y]=x; ch[x][ys^1]=y; ch[y][ys]=B; 
    		if(B) fa[B]=y; fa[x]=R; fa[y]=x;
    		upd(y);upd(x);
    	}
    	void splay(ll x)
    	{
    		ll y=x,z,top=0;sta[++top]=y;
    		while(nort(y)) y=fa[y],sta[++top]=y;
    		while(top) pd(sta[top--]);
    		while(nort(x))
    		{
    			y=fa[x];z=fa[y];
    			if(nort(y))
    				rotate((ch[z][1]==y)==(ch[y][1]==x)?y:x);
    			rotate(x);
    		}
    	}
    	void access(ll x)
    	{
    		for(ll y=0;x;y=x,x=fa[x])
    			splay(x),rs=y,upd(x);
    	}
    //	void makert(ll x){	access(x);splay(x);rev(x);}
    	void split(ll y){ access(y);splay(y);}
    	void add(ll x)
    	{
    		split(x);rev(x,1);
    	}
    	ll ask(ll x)
    	{
    		split(x);return s[x];
    	}
    }lct;
    struct Qry
    {
    	ll r,z,id;
    	bool operator<(Qry w)const
    	{
    		return r<w.r;
    	}
    }q[N];
    ll ans[N],pos;
    inline ll ksm(ll x,ll y)
    {
    	ll res=1;
    	while(y){ if(y&1) res=(ll)res*x%mod;x=(ll)x*x%mod;y>>=1;}
    	return res;
    }
    struct skr
    {
    	ll to,nxt;
    }a[N<<1];
    ll head[N],cnt;
    inline void add(ll x,ll y)
    {
    	a[++cnt].to=y;a[cnt].nxt=head[x];head[x]=cnt;
    }
    inline void dfs(ll x)
    {
    	for(ll i=head[x];i;i=a[i].nxt)
    	{
    		ll y=a[i].to;
    		if(d[y]) continue;
    		d[y]=d[x]+1;
    		dfs(y);
    	}
    }
    int main()
    {
    	freopen("poetry.in","r",stdin);
    	freopen("poetry.out","w",stdout);
    	n=read();m=read();k=read();
    	for(ll i=2;i<=n;i++)
    	{
    		ll x=read();
    		add(x,i);
    		add(i,x);
    		lct.fa[i]=x;
    	}
    	d[1]=1;
    	dfs(1);
    	for(ll i=1;i<=n;i++) d[i]=ksm(d[i],k);
    	for(ll i=1;i<=n;i++)
    		b[i]=(d[i]-d[lct.fa[i]]+mod)%mod;
    	for(ll i=1;i<=m;i++)
    	{
    		ll x=read(),y=read();
    		q[i]=(Qry){x,y,i};
    	}
    	sort(q+1,q+1+m);
    	for(ll i=1;i<=m;i++)
    	{
    		while(pos+1<=q[i].r) pos++,lct.add(pos);
    		ans[q[i].id]=lct.ask(q[i].z)%mod;
    	}
    	for(ll i=1;i<=m;i++)
    		printf("%lld
    ",ans[i]);
    	fclose(stdin);
    	fclose(stdout);
    }
    
  • 相关阅读:
    查看mysql数据库引擎
    crontab 从nano 转换为 vim
    Linux中,去掉终端显示的当前目录的绝对路径
    nginx 卸载后重新安装/etc/nginx配置文件没了,cannot open /etc/nginx/nginx.conf (No such file or directory)
    rabbitmq 配置
    OSError: mysql_config not found
    No module named 'ConfigParser'
    windows 安装tensorflow
    微服务架构设计
    centOS rabbitmq 安装
  • 原文地址:https://www.cnblogs.com/yudes/p/12183402.html
Copyright © 2011-2022 走看看