日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

NOI.AC#2266-Bacteria【根号分治,倍增】

發(fā)布時間:2023/12/3 C# 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NOI.AC#2266-Bacteria【根号分治,倍增】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

正題

題目鏈接:http://noi.ac/problem/2266


題目大意

給出nnn個點的一棵樹,有一些邊上有中轉(zhuǎn)站(邊長度為222,中間有一個中轉(zhuǎn)站),否則就是邊長為111

mmm次詢問一個東西從xxx出發(fā)走到yyy,每隔kkk步中轉(zhuǎn)站會關閉一次(kkk的倍數(shù)步走完后不能在中轉(zhuǎn)站上)。求在關閉多少次以內(nèi)可以到達

1≤n,m≤1051\leq n,m\leq 10^51n,m105


解題思路

發(fā)現(xiàn)最多只需要走2n2n2n步,然后每隔kkk步關閉一次,所以可以考慮根號分治。

先處理好總的倍增數(shù)組,后面求LCALCALCA和跳鏈要用。

對于k=1k=1k=1的詢問,就看一下中間有沒有中轉(zhuǎn)站,如果有就是?1-1?1否則就是距離

對于k≤nk\leq \sqrt nkn?的詢問,我們對于每個kkk都進行一次預處理,處理每個周期每個點往上走能走到哪里。然后再處理一個倍增數(shù)組,然后詢問的時候就在上面跳就好了

對于k>nk>\sqrt nk>n?的詢問直接每次暴力跳kkk步如果是中轉(zhuǎn)站就跳k?1k-1k?1步,然后一直跳到LCALCALCA

時間復雜度O(nnlog?n)O(n\sqrt n\log n)O(nn?logn),調(diào)一下塊的大小就能過了


code

#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=2e5+10,T=17; struct edge{int to,next; }a[N<<1]; struct node{int x,y,k,id; }q[N]; int n,m,Q,tot,num,ans[N],ls[N],dep[N],sd[N]; int g[N][100],f[N][T+1],h[N][T+1]; void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return; } bool cmp(node x,node y) {return x.k<y.k;} void dfs(int x,int fa){g[x][0]=x;sd[x]=sd[fa]+(x>n);f[x][0]=fa;dep[x]=dep[fa]+1;for(int i=1;i<=Q;i++)g[x][i]=g[fa][i-1];for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;dfs(y,x);}return; } int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0]; } void calc(int x,int fa,int k){if(g[x][k]>n)h[x][0]=g[x][k-1];else h[x][0]=g[x][k];for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;calc(y,x,k);}return; } int query(int x,int y,int k){int p=LCA(x,y),ans=0;for(int i=T;i>=0;i--){if(dep[h[x][i]]>dep[p])x=h[x][i],ans+=(1<<i);if(dep[h[y][i]]>dep[p])y=h[y][i],ans+=(1<<i);}if(x!=y){int dis=dep[x]+dep[y]-2*dep[p];if(dis>=0&&dis<=k)ans++;else if(dis>k) ans+=2;}return ans; } int getf(int x,int k){for(int i=0;i<=T;i++)if((k>>i)&1)x=f[x][i];return x; } int solve(int x,int y,int k){int p=LCA(x,y),ans=0;while(dep[x]>dep[p]){int z=getf(x,k-1),t;if(f[z][0]>n)t=z;else t=f[z][0];if(dep[t]>dep[p])x=t,ans++;else break;}while(dep[y]>dep[p]){int z=getf(y,k-1),t;if(f[z][0]>n)t=z;else t=f[z][0];if(dep[t]>dep[p])y=t,ans++;else break;}if(x!=y){int dis=dep[x]+dep[y]-2*dep[p];if(dis>=0&&dis<=k)ans++;else if(dis>k) ans+=2;}return ans; } int main() {scanf("%d",&n);num=n;for(int i=1;i<n;i++){int x,y,w;scanf("%d%d%d",&x,&y,&w);if(w==1)addl(x,y),addl(y,x);else{++num;addl(x,num);addl(num,y);addl(y,num);addl(num,x);}}Q=sqrt(n);if(Q>=70)Q=70;scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].k);q[i].id=i;}sort(q+1,q+1+m,cmp);dfs(1,0);for(int j=1;j<=T;j++)for(int i=1;i<=num;i++)f[i][j]=f[f[i][j-1]][j-1];int l=1,r=1;for(;r<=m&&q[r].k<=Q;r++,l=r){while(r<m&&q[r].k==q[r+1].k)r++;if(q[r].k==1){for(int i=l;i<=r;i++){int x=q[i].x,y=q[i].y,lca=LCA(x,y);if(sd[x]+sd[y]-2*sd[lca])ans[q[i].id]=-1;else ans[q[i].id]=dep[x]+dep[y]-2*dep[lca];}continue;}calc(1,1,q[r].k);for(int j=1;j<=T;j++)for(int i=1;i<=num;i++)h[i][j]=h[h[i][j-1]][j-1];for(int i=l;i<=r;i++)ans[q[i].id]=query(q[i].x,q[i].y,q[i].k);}for(int i=r;i<=m;i++)ans[q[i].id]=solve(q[i].x,q[i].y,q[i].k);for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結

以上是生活随笔為你收集整理的NOI.AC#2266-Bacteria【根号分治,倍增】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。