JZOJ 5930 調試喫土記

題目

給定一棵樹。每個節點有一個權值a[i],詢問節點x和一個數字c,求c與x的子樹中與x深度值之差小於k的點的權值的最大公約數之積。

題解

爆0代碼欣賞

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100010
#define M 10000010
#define N1 670010
#define mo 998244353
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
	int to,next;
}edge[N<<1];
struct noq{
	int x,w;
}que[N];
struct no1{
	int p,e,x,xs,id;
}q1[N*60];
LL tr1[33],tr2[33];
int tot,head[N];
int pri[N1],f[M],c[M];
int fa[N],dep[N],qu[N],bfn[N],siz[N];
bool bz[M];
int n,q,k,Ans,cs,ans[N][31];
int u,v,x,w,las,wz;
int i,j,l,m,CNT,TOT,t2,t3,a1,a2;
int zl[N*30],cx;
bool p30;
void lb(int x,int y){
	edge[++tot].to=y;
	edge[tot].next=head[x];
	head[x]=tot;
}
int lowbit(int x){return x&(-x);}
void add(int x,LL z){
	for(;x<=30;x=x+lowbit(x))tr1[x]=tr1[x]+z;
}
void add0(int x,LL z){
	for(;x<=30;x=x+lowbit(x))tr2[x]=tr2[x]+z;
}
LL qry(int x){
	LL rs=0;
	for(;x;x=x-lowbit(x))rs=rs+tr1[x];
	return rs;
}
LL qry0(int x){
	LL rs=0;
	for(;x;x=x-lowbit(x))rs=rs+tr2[x];
	return rs; 
}
int ksm(int x,int y){
	int rs=1;
	for(;y;y>>=1,x=(1ll*x*x)%mo)
	if(y&1)rs=(1ll*rs*x)%mo;
	return rs;
}
void pre(){
	int i,j;
	fo(i,2,M-10){
		if(!bz[i]){
			pri[++CNT]=i;
			f[i]=i;
		}
		for(j=1;j<=CNT&&i*pri[j]<=M-10;j++){
			bz[i*pri[j]]=1;
			f[i*pri[j]]=pri[j];
			if(i%pri[j]==0)break;
		}
	}
}
void bfs(int x){
	int ql=0,qr=1,i;
	qu[1]=x;
	while(ql<qr){
		x=qu[++ql];
		bfn[x]=ql;
		siz[x]=1;
		for(i=head[x];i;i=edge[i].next)
		    if(fa[x]^edge[i].to){
		    	fa[edge[i].to]=x;
		    	dep[edge[i].to]=dep[x]+1;
		    	qu[++qr]=edge[i].to;
			}
	}
}
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
bool cmp(no1 a,no1 b){return a.p<b.p||(a.p==b.p&&a.x<b.x);}
int main(){
    scanf("%d%d%d",&n,&q,&k);
    fo(i,1,n)scanf("%d",&c[i]);
    fo(i,1,n-1){
    	scanf("%d%d",&u,&v);
    	lb(u,v);lb(v,u);
	}
	bfs(1);
	fd(i,n,1){
		siz[fa[qu[i]]]+=siz[qu[i]];
	}
	pre();
    if(n<=1000){
	    fo(cs,1,q){
	    	scanf("%d%d",&x,&w);
	    	l=bfn[x];
	    	Ans=1;
	    	while(l<=n){
	    		if(dep[qu[l]]-dep[x]>=k||(dep[qu[l]]<=dep[x]&&(qu[l]^x)))break;
	    		Ans=(1ll*Ans*gcd(w,c[qu[l]]))%mo;
	    		l++;
			}
			printf("%d\n",Ans);
		}
	    return 0;
	}
	fo(i,1,q){
		scanf("%d%d",&que[i].x,&que[i].w);
		u=que[i].w;
		while(u^1){
			zl[++t2]=f[u];
			u=u/f[u];
		}
	}
	sort(zl+1,zl+t2+1);
	cx=unique(zl+1,zl+t2+1)-zl-1;
	if(cx<=30){
		TOT=0;
		fo(i,1,n){
			u=c[i];
			las=0;
		    while(u^1){
		    	if(f[u]^las){
					q1[++TOT].p=f[u];
					q1[TOT].e=1;
					q1[TOT].xs=2;
					q1[TOT].x=bfn[i];
				}else q1[TOT].e++;
				las=f[u];
			    u=u/f[u];
		    }
		}
		fo(i,1,q){
			u=que[i].w;
			if(u==1)continue;
			las=0;
			a1=TOT+1;
			while(u^1){
				if(f[u]^las){
					q1[++TOT].p=f[u];
					q1[TOT].e=1;
					q1[TOT].x=bfn[que[i].x]-1;
					q1[TOT].xs=-1;
					q1[TOT].id=i;
				}else q1[TOT].e++;
				las=f[u];
				u=u/f[u];
			}
			a2=TOT;
			int L1=bfn[que[i].x],R1=L1+siz[que[i].x]-1,Mid;
			while(L1<=R1){
				Mid=(L1+R1)>>1;
				if(dep[qu[Mid]]-dep[que[i].x]<k)w=Mid,L1=Mid+1;else R1=Mid-1;
			}
			fo(j,a1,a2){
				q1[++TOT]=q1[j];
				q1[TOT].x=w;
				q1[TOT].xs=1;
			}
		}
        sort(q1+1,q1+TOT+1,cmp);
		int wz1=1,wl,wr;
		wz=1;
		fo(i,1,cx){
			while(wz<=TOT&&q1[wz].p<zl[i])wz++;
			wl=-1;
			while(wz<=TOT&&q1[wz].p==zl[i]){
				if(wl==-1)wl=wz;wr=wz;
				if(q1[wz].xs==2){
					add(q1[wz].e,q1[wz].e);
				    add0(q1[wz].e,1);
				}else{
					w=qry0(30)-qry0(q1[wz].e-1);
					ans[q1[wz].id][i]+=1ll*w*q1[wz].xs*q1[wz].e;
					w=qry(q1[wz].e-1);
					ans[q1[wz].id][i]+=1ll*w*q1[wz].xs;
				}
				wz++;
			}
			fo(j,wl,wr)if(q1[j].xs==2){
				add(q1[j].e,-q1[j].e);
				add0(q1[j].e,-1);
			}
		}
		fo(i,1,q){
			Ans=1;
			fo(j,1,cx){
				w=ksm(zl[j],ans[i][j]);
				Ans=(1ll*Ans*w)%mo;
			}
			printf("%d\n",Ans);
		}
		return 0;
	}
	fo(cs,1,q){
	    l=bfn[x];
	    Ans=1;
	    while(l<=n){
	    	if(dep[qu[l]]-dep[x]>=k||(dep[qu[l]]<=dep[x]&&(qu[l]^x)))break;
	    	Ans=(1ll*Ans*gcd(w,c[qu[l]]))%mo;
	    	l++;
		}
		printf("%d\n",Ans);
	}
	return 0;
}

Para. 1

A這題的過程十分坎坷。
比賽的時候差一點點AC了。就只有2個地方想錯了,其他地方和題解基本一樣。
錯誤的地方:①查詢x的子樹中與x深度值之差小於k的點,這些點並不一定對應着bfs序中的一段TOT。。。。
②質數雖然很多,但是總操作數不會超過O(8n)O(8n),所以按照比賽的時候的打法,然後改一下就可以AC了。

Para. 2

苦逼的我選擇重新打一遍。。。。
這道題目就幾個點。
將詢問和修改一起搞。修正錯誤②,通過排序,固定住一個質數。指數之間互不影響。
考慮在dfs序上做文章,那麼修改有兩個,在x打個+1標記,在x的k祖先打一個-1標記。
然後轉化成二維數點問題了。
如何離線做?離線,也就是轉化成差分問題。每個詢問拆成2個,一個是-的,一個是+的。

Para. 3

涉及了幾個小細節,發現我也進步了好多啊。
查詢區間裏min(x,y)min(x,y)\sum min(x,y)*min(x,y).
類比JZOJ 5919,求i&lt;aii\sum_{i&lt;a}i的出現次數*i中的一段和大於等於數a的個數。
固定住一個質數後,再按dfs序從小到大排序。然後用兩個樹狀數組求出這個質數對答案的貢獻。兩樹狀數組——分類求值。
最後快速冪一下即可。

Para. 4

本人輸就輸在沒有好好計算時間複雜度。
總共O(8n)O(8n)個操作點,排序O(24n log n)O(24n\ log\ n),樹狀數組log 30log\ 30,快速冪一個loglog
所以總時間複雜度不會超過O(24n log n)O(24n\ log\ n)

Para. 5

調試調了很多個小時。無論怎麼集中精力,在碼題的時候都會有地方寫錯。
錯誤之處:AC代碼中的fk[tar[cz[j].x]]fk[tar[cz[j].x]]寫成了fk[cz[j].x]fk[cz[j].x]
有9個點WA了。
處理措施1:拍小數據。小數據拍過了,拍隨機的大數據。但是無任何進展。
處理措施2(比較新穎的調試方式,在陷入很深的困境的時候使用)。
前提:對程序的整個過程十分熟悉。
答案中,質因子2的個數錯了,所以只看2。
答案的計算只涉及到整個dfs序中的某一小部分,然後分別將正確和錯誤的程序中,每個點對答案的貢獻寫出來。如下面兩張圖。
如果遍歷點的順序不一樣,還需要特意地排個序。
在這裏插入圖片描述
在這裏插入圖片描述
每個打了調試標記的地方都去設個斷點。
在這個時候調試終於有了進展。
在這裏插入圖片描述
在這裏插入圖片描述
發現第144行進不去,找到錯誤了。
大功告成!

Para. n

虛樹也是可以搞的。
如果不能用普通的調試方法調出程序,那麼嘗試一些腦洞打開的方法。

前提:對程序的整個過程十分熟悉!!!

AC代碼欣賞

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
#define N1 2000010
#define M 10000010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define mo 998244353
using namespace std;
struct note{
	int to,next;
}edge[N<<1];
int tot,head[N];
struct noc{
	int p,e,x,tp,id;
}cz[N1];
int i,j,k,l,n,m,q,las,TOT,CNT;
int u,v,wl,wr,w,ans[N];
int pri[670010],f[M],fa[17][N];
int fk[N],T,dfn[N],tar[N],siz[N];
int tr[33],tr1[33],cc[N];
bool bz[M];
int ksm(int x,long long y){
	y=y%(mo-1);
	int rs=1;
	for(;y;y>>=1,x=(1ll*x*x)%mo)if(y&1)rs=(1ll*rs*x)%mo;
	return rs;
}
void pre(){
	int i,j;
	fo(i,2,M-10){
		if(!bz[i]){
			pri[++TOT]=i;
			f[i]=i;
		}
		for(j=1;j<=TOT&&i*pri[j]<=M-10;j++){
			bz[i*pri[j]]=1;
			f[i*pri[j]]=pri[j];
			if(i%pri[j]==0)break;
		}
	}
}
void lb(int x,int y){
	edge[++tot].to=y;
	edge[tot].next=head[x];
	head[x]=tot;
}
void dg(int x){
	int i;
	dfn[x]=++T;tar[T]=x;
	siz[x]=1;
	for(i=head[x];i;i=edge[i].next)
	    if(edge[i].to^fa[0][x]){
	    	fa[0][edge[i].to]=x;
	    	dg(edge[i].to);
	    	siz[x]=siz[x]+siz[edge[i].to];
		}
}
bool cmp(noc a,noc b){
	return a.p<b.p||(a.p==b.p&&a.x<b.x)||(a.p==b.p&&a.x==b.x&&a.tp<b.tp);
}
int lowbit(int x){return x&(-x);}
void add(int x,int num){
	for(;x<=30;x=x+lowbit(x))tr[x]=tr[x]+num;
}
int qry(int x){
	int rs=0;
	for(;x;x=x-lowbit(x))rs=rs+tr[x];
	return rs;
}
void add0(int x,int num){
	for(;x<=30;x=x+lowbit(x))tr1[x]=tr1[x]+num;
}
int qry0(int x){
	int rs=0;
	for(;x;x=x-lowbit(x))rs=rs+tr1[x];
	return rs;
}
int main(){
	scanf("%d%d%d",&n,&q,&k);
	pre();
	fo(i,1,n)scanf("%d",&cc[i]);
	fo(i,1,n-1){
		scanf("%d%d",&u,&v);
		lb(u,v);lb(v,u);
	}
	dg(1);
	fo(j,1,16)fo(i,1,n)fa[j][i]=fa[j-1][fa[j-1][i]];
	fo(i,1,n){
		u=i;
		fd(j,16,0)if((1<<j)&k)u=fa[j][u];
		fk[i]=u;
	}
	fo(i,1,n){
		m=cc[i];
		if(m==1)continue;
		wl=CNT+1;
		las=0;
		while(m^1){
			if(f[m]^las){
				cz[++CNT].p=f[m];
				cz[CNT].e=1;
				cz[CNT].x=dfn[i];
				cz[CNT].tp=0;
			}else cz[CNT].e++;
			las=f[m];
			m=m/f[m];
		}
		wr=CNT;
		fo(j,wl,wr)if(fk[tar[cz[j].x]]){
			cz[++CNT].p=cz[j].p;
			cz[CNT].e=cz[j].e;
			cz[CNT].x=dfn[fk[tar[cz[j].x]]];
			cz[CNT].tp=-1;
		}
	}
	fo(i,1,q){
		ans[i]=1;
		scanf("%d%d",&u,&m);
		if(m==1)continue;
		wl=CNT+1;
		las=0;
		while(m^1){
			if(f[m]^las){
				cz[++CNT].p=f[m];
				cz[CNT].e=1;
				cz[CNT].x=u;
				cz[CNT].tp=1;
				cz[CNT].id=i;
			}else cz[CNT].e++;
			las=f[m];
			m=m/f[m];
		}
		wr=CNT;
		fo(j,wl,wr){
			cz[++CNT].p=cz[j].p;
			cz[CNT].e=cz[j].e;
			cz[CNT].x=dfn[cz[j].x]+siz[cz[j].x]-1;
			cz[CNT].tp=2;
			cz[CNT].id=i;
			cz[j].x=dfn[cz[j].x]-1;
		}
	}
	sort(cz+1,cz+CNT+1,cmp);
	i=1;
	for(;i<=CNT;i++){
		wl=-1;
		while(i<=CNT){
			if(wl==-1)wl=i;
			wr=i;
			if(cz[i].tp<1){
				if(cz[i].tp==0){
					add(cz[i].e,cz[i].e);
				    add0(cz[i].e,1);
				}else{
					add(cz[i].e,-cz[i].e);
				    add0(cz[i].e,-1);
				}		
			}else{
				if(cz[i].tp==1&&cz[i].x==0){
					i++;
					continue;
				}
				w=qry(cz[i].e);
				w=w+(qry0(30)-qry0(cz[i].e))*cz[i].e;
				if(cz[i].tp==1)w=-w;
				if(w>0){
					ans[cz[i].id]=(1ll*ans[cz[i].id]*ksm(cz[i].p,w))%mo;
				}else{
					ans[cz[i].id]=(1ll*ans[cz[i].id]*ksm(cz[i].p,1ll*-w*(mo-2)))%mo;
				}
			}
			if(cz[i+1].p==cz[i].p)i++;
			else break;
		}
		wr=i;
		fo(j,wl,wr){
			if(cz[j].tp==-1){
				add(cz[j].e,cz[j].e);
				add0(cz[j].e,1);
			}
			if(cz[j].tp==0){
				add(cz[j].e,-cz[j].e);
				add0(cz[j].e,-1);
			}
		}
	}
	fo(i,1,q)printf("%d\n",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章