1. 程式人生 > 其它 >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;
}