P4616 [COCI2017-2018#5] Pictionary

时间:2021-09-07
本文章向大家介绍 P4616 [COCI2017-2018#5] Pictionary,主要包括 P4616 [COCI2017-2018#5] Pictionary使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

$ \texttt{Introduction} $

原题+思维

$ \texttt{Solution} $

本题题意可以简化为 $ i $ 和 $ j $ 之间连一条 $ m-\gcd(i,j)+1 $ 的边,然后问你从 $ a $ 走到 $ b $ 的道路中的最大值的最小值是多少。

简化之后的问题,难度就在于建边了,首先肯定不能建出一张单纯的图,我们发现答案要尽量的小,所以我们按价值从小到大加边,如果两个点已经相连,那么便不用再连,最后我们会得出一棵树。

但大体的思路怎么具体实现呢?

首先有一个很 $ naive $ 的想法,我们从大到小枚举最大公约数,然后找到最大公约数为这个数的两个点,按照上述的方法连边,但是这样时间复杂度会爆炸,亲测只有 $ 32pts $,那怎么办呢?

我们可以发现,选取的两个数一定都是最大公约数的倍数,那么我们直接将最大公约数和他的倍数连边,这样就把最大公约数是这个数的情况的边都连好了,也许你要问那如果会连多余的边呢,其实不会因为我们是要判断过得,如果最大公约数比这个数要大,那么前面一定已经连过边,就不会再连了。

建好这棵树之后就是经典操作了,我们要找两点之间的边的最大值,可以用倍增实现,具体可以参考[NOIP2013 提高组] 货车运输,然后这道题就做好了。

int find(int x)
{
	if (ff[x]==x) return ff[x];
	ff[x]=find(ff[x]);return ff[x];
}
void add(int x,int y,int z)
{
	cnt++;a[cnt]=y;b[cnt]=d[x];c[cnt]=z;d[x]=cnt;
}
void sc(int x,int y) //找两点之间最大边
{
    int t,i;
    if (e[x]>e[y]){
        t=x;x=y;y=t;
    }
for (i=lg[n];i>=0;i--)
     if (e[f[y][i]]>=e[x])
          {
              ans=max(ans,u[y][i]);
              y=f[y][i];
          }
    if (x==y) return ;
    for (i=lg[n];i>=0;i--)
         if (f[x][i]!=f[y][i])
              {
                  ans=max(ans,max(u[x][i],u[y][i]));
                  x=f[x][i];y=f[y][i];
              }
    ans=max(ans,max(u[x][0],u[y][0]));
}
int main()
{
	//ios::sync_with_stdio(0);cin.tie();cout.tie();
	n=read();m=read();T=read();
	for (i=1;i<=n;i++) ff[i]=i;
	for (i=m;i>=1;i--)
	     {
	     	for (j=2;j<=n/i;j++)
	     	              {
	     	              	x=i;y=i*j;
	     	              	r1=find(x);r2=find(y);
	     	              	if (r1!=r2)
	     	              	    {
	     	              	    	add(x,y,m-i+1);add(y,x,m-i+1);
	     	              	    	tot++;
								   }
							ff[r1]=r2;
							if (tot==n-1) break;
						   }
			if (tot==n-1) break;
	}//建图
	for (i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    t=1;w=1;f2[1]=1;e[1]=1;
    while (t<=w)
        {
            for (i=d[f2[t]];i;i=b[i])
                 {
                     if (e[a[i]]==0)
                          {
                              e[a[i]]=e[f2[t]]+1;
                              f[a[i]][0]=f2[t];u[a[i]][0]=c[i];
                              for (j=1;j<=lg[n];j++)
                                  {
                                  f[a[i]][j]=f[f[a[i]][j-1]][j-1];
                                  u[a[i]][j]=max(u[a[i]][j-1],u[f[a[i]][j-1]][j-1]);
                                  }
                            w++;f2[w]=a[i];
                            
                          }
                 }
            t++;
        }//倍增的预处理
    for (;T;T--)
          {
          	x=read();y=read();ans=0;
          	sc(x,y);
          	printf("%d\n",ans);
		  }
    return 0;
}

原文地址:https://www.cnblogs.com/huangxuze/p/15237752.html