对于今天比赛的总结
逆向,考虑增加一条边,将两个连通块合并了
考虑新连通块的直径与原来两个连通块直径的关系
设新连通块的直径端点为 x, y,原两个连通块的直径端点为a, b, c, d。
容易发现,x, y 一定是 a, b, c, d 其中之一。
除去原先两个连通块直径长度,乘上新连通块直径长度(除法需乘法逆元)
连通性用并查集维护,树上路径长度计算用 LCA+ 预处理前缀长度。
时间复杂度为 O(n log n)
(↑以上解释非原创!!!∑(゚Д゚ノ)ノ)
#include<iostream> #include<cstdio> using namespace std; int N,f[1000001]; unsigned long long val[1000001],h[1000001],lng[1000001]; unsigned long long MOD=1000000007,e[1000001]; int top[1000001],heavy_son[1000001],q[1000001],F[1000001],dep[1000001],size[1000001],head[4000001]; unsigned long long total,c[1000001][3]; struct node{ int l,r; }a[1000001],b[1000001]; struct Node{ int to,next; }d[1000001]; int add(int u,int v){ d[++N].to=v; d[N].next=head[u]; head[u]=N; return 0; } int dad(int x){ return f[x]==x ? x : f[x]=dad(f[x]); } unsigned long long inv(unsigned long long xx){ return xx==1 ? 1 : ((MOD-MOD/xx)*inv(MOD%xx)+MOD)%MOD; } int dfs1(int father,int root,int depth,unsigned long long val){ lng[root]=val; F[root]=father; dep[root]=depth; size[root]=1; for(int i=head[root];i;i=d[i].next){ int To=d[i].to; if(To==father) continue; dfs1(root,To,depth+1,val+h[To]); size[root]+=size[To]; if(size[To]>size[heavy_son[root]]) heavy_son[root]=To; } return 0; } int dfs2(int father,int root,int Top){ top[root]=Top; if(heavy_son[root]!=0) dfs2(root,heavy_son[root],Top); for(int i=head[root];i;i=d[i].next){ int To=d[i].to; if(To==father||To==heavy_son[root]) continue; dfs2(root,To,To); } return 0; } int lca(int X,int Y){ while(top[X]!=top[Y]){ if(dep[top[X]]>dep[top[Y]]) X=F[top[X]]; else Y=F[top[Y]]; } return dep[X]<dep[Y] ? X : Y; } int main(){ int n; cin>>n; total=1; for(int i=1;i<=n;i++){ cin>>h[i]; val[i]=h[i]; total=(total*h[i])%MOD; b[i].l=i; b[i].r=i; } for(int i=1;i<=n-1;i++){ cin>>a[i].l>>a[i].r; add(a[i].l,a[i].r); add(a[i].r,a[i].l); } for(int i=1;i<=n-1;i++) cin>>q[i]; for(int i=1;i<=n;i++) f[i]=i; dfs1(0,1,1,h[1]); dfs2(1,1,1); e[n]=total; for(int i=n-1;i>=1;i--){ int j=q[i]; int dad1=dad(a[j].l); int dad2=dad(a[j].r); f[dad2]=dad1; int x1=b[dad1].l; int x2=b[dad1].r; int x3=b[dad2].l; int x4=b[dad2].r; unsigned long long maxn=0; int p1=0,p2=0; if(x1!=x2){ int z=lca(x1,x2); if(lng[x1]+lng[x2]-2*lng[z]+h[z]>maxn){ maxn=lng[x1]+lng[x2]-2*lng[z]+h[z]; p1=x1; p2=x2; } } if(x1!=x3){ int z=lca(x1,x3); if(lng[x1]+lng[x3]-2*lng[z]+h[z]>maxn){ maxn=lng[x1]+lng[x3]-2*lng[z]+h[z]; //cout<<; p1=x1; p2=x3; } } if(x1!=x4){ int z=lca(x1,x4); if(lng[x1]+lng[x4]-2*lng[z]+h[z]>maxn){ maxn=lng[x1]+lng[x4]-2*lng[z]+h[z]; p1=x1; p2=x4; } } if(x2!=x3){ int z=lca(x2,x3); if(lng[x3]+lng[x2]-2*lng[z]+h[z]>maxn){ maxn=lng[x3]+lng[x2]-2*lng[z]+h[z]; p1=x3; p2=x2; } } if(x2!=x4){ int z=lca(x2,x4); if(lng[x4]+lng[x2]-2*lng[z]+h[z]>maxn){ maxn=lng[x4]+lng[x2]-2*lng[z]+h[z]; p1=x4; p2=x2; } } if(x3!=x4){ int z=lca(x3,x4); if(lng[x3]+lng[x4]-2*lng[z]+h[z]>maxn){ maxn=lng[x3]+lng[x4]-2*lng[z]+h[z]; p1=x3; p2=x4; } } e[i]=e[i+1]*maxn%MOD*inv(val[dad1])%MOD*inv(val[dad2])%MOD; val[dad1]=maxn; b[dad1].l=p1; b[dad1].r=p2; } for(int i=1;i<=n;i++){ cout<<e[i]<<'\n'; } return 0; }
T1
可以通过线性筛筛去 106 以下的质数。
如果此时剩下的数 x 不是 1,由于 c ≤ 1018,则必定是至多两个大于 106 的质数的乘积。
此时只要判断这两个质数是否相等即可,对 x 开根再平方看是否等于 x 即可。
(↑以上解释仍非原创!!!∑(゚Д゚ノ)ノ)
//来自King_8的代码(Genius改编) #include<iostream> #include<cstdio> #include<queue> #include<cmath> #include<algorithm> using namespace std; int c[1000010],pri[1000010],N; long long int n; void Pri(){ for(int i=2;i<=1000000;i++){ if(c[i]==0){ pri[++N]=i; for(int j=2;j*i<=1000000;j++) c[j*i]=1; } } } bool check(long long int x){ int i=1; long long int j=sqrt(x); while(i<=N&&x!=1&&pri[i]<=j){ if(x%pri[i]==0) x/=pri[i]; while(x%pri[i]==0){ x/=pri[i]; return true; } i++; } j=sqrt(x); if(j*j==x&&x>1000000) return true; return false; } int main(){ Pri(); int T; scanf("%d",&T); while(T--){ scanf("%lld",&n); if(check(n)) printf("yes\n"); else printf("no\n"); } }

浙公网安备 33010602011771号