樹的直徑,LCA複習筆記
樹的(直徑,LCA)複習
前言
複習筆記第6篇。
求直徑的兩種方法
樹形DP:
dfs(y); ans=max( ans,d[x]+d[y]+w[i] ); d[x]=max( d[x],d[y]+w[i] );
int dis=dfs( v,u )+1; if ( f[u]<dis ) g[u]=f[u],f[u]=dis; else if ( g[u]<dis ) g[u]=dis; ans=max( ans,f[u]+g[u]+1 ); return f[u];
兩次 bfs/dfs:
從任意點出發,找到最遠點l;
從 l 出發,找到最遠點 r. \(l\to r\) 即為所求。
0——P4408 [NOI2003]逃學的小孩
題意
\(n\) 點 \(m\) 邊的帶權無向圖,任意兩點之間有且僅有一條通路。有三個點:A,B,C,從 C 出發,先去 AB 中較近的一個,如果沒找到,再去另一個,不給出具體的 ABC,問最壞情況下要多長時間。
思路
題目要求就是在一棵樹上找到3個點 \(A\) 、\(B\)、\(C\) 令 \(AB+BC\) 最大,同時要滿足 \(AC>AB\)。
由於要最大化這個距離,一個很明顯的想法就是讓其中一條成為直徑,設為 \(AB.\)
程式碼
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=2e5+10; struct edge { int to,nxt;ll val; }e[N*10]; int n,m,head[N],tot=0,vis[N]; ll dis[N],dis2[N]; void add( int u,int v,ll w ) { tot++; e[tot].to=v; e[tot].nxt=head[u]; e[tot].val=w; head[u]=tot; } int bfs( int S ) { memset( dis,0,sizeof(dis) ); memset( vis,0,sizeof(vis) ); queue<int> q; while ( !q.empty() ) q.pop(); q.push( S ); vis[S]=1; int res=0,num=0; while ( q.size() ) { int u=q.front(); q.pop(); for ( int i=head[u]; i; i=e[i].nxt ) { int v=e[i].to; if ( vis[v] ) continue; vis[v]=1; dis[v]=dis[u]+e[i].val; q.push( v ); if ( dis[v]>res ) res=dis[v],num=v; } } return num; } int main() { scanf( "%d%d",&n,&m ); for ( int i=1; i<=m; i++ ) { int u,v; ll w; scanf( "%d%d%lld",&u,&v,&w ); add( u,v,w ); add( v,u,w ); } int l=bfs(1),r=bfs(l); ll ans=dis[r],res=0; for ( int i=1; i<=n; i++ ) dis2[i]=dis[i]; bfs( r ); for ( int i=1; i<=n; i++ ) res=max( res,min(dis[i],dis2[i]) ); printf( "%lld",ans+res ); }
1——P2491 [SDOI2011]消防
題意
\(n\) 個城市,任意兩個都連通且有唯一路徑,每條連通兩個城市的道路的長度為 \(z_i\). 在一條邊長度和不超過 \(s\) 的路徑(兩端都是城市)上建立消防樞紐,要求其他所有城市到這條路徑的距離的最大值最小。求樞紐位置。
思路
每一個點到樹上最遠的點一定在直徑上.所以可以直接列舉直徑上的點,然後在找直徑上的點到其他點的距離最大是多少.一個很顯然的想法是,在直徑上取的距離越大越優,那麼可以直接列舉起點。
但是 \(N^2\) 複雜度還是不夠。顯然,答案具有單調性(不超過 s 的情況下越長越好),那麼可以通過雙指標,單調佇列優化,\(O(n)\) 實現這個過程。當然也可以二分不過要帶一個 \(\log.\)
程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=3e5+10;
const ll inf=1e15+10;
struct edge
{
ll fro,to,nxt; ll val;
}e[N<<1];
ll n,s,tot,head[N],dis[N],pre[N],bet[N],l,r;
ll ans=inf,sum[N],dis1[N],que[N]={inf},t=0,h=0;
void add( ll u,ll v,ll w )
{
e[++tot].fro=u; e[tot].to=v; e[tot].val=w; e[tot].nxt=head[u]; head[u]=tot;
}
void dfs( ll u,ll fa,ll sum,bool sav )
{
if ( sav ) pre[u]=fa,bet[u]=sum;
dis[u]=dis[fa]+sum;
for ( ll i=head[u]; i; i=e[i].nxt )
if ( e[i].to!=fa ) dfs( e[i].to,u,e[i].val,sav );
}
void get_path()
{
dfs( 1,0,0ll,0 ); ll mx=0;
for ( ll i=1; i<=n; i++ )
if ( dis[i]>mx ) l=i,mx=dis[i];
dfs( l,0,0ll,1 ); mx=0;
for ( ll i=1; i<=n; i++ )
if ( dis[i]>mx ) r=i,mx=dis[i];
}
void bfs()
{
memset( dis,63,sizeof(dis) ); queue<ll> q,fro;
for ( ll i=r; i; i=pre[i] )
q.push(i),fro.push(i),dis[i]=0;
while ( !q.empty() )
{
ll v=q.front(),u=fro.front(); q.pop(); fro.pop();
for ( ll i=head[v]; i; i=e[i].nxt )
if ( dis[e[i].to]>=inf )
{
dis[e[i].to]=dis[v]+e[i].val;
dis1[u]=max( dis1[u],dis[e[i].to] );
q.push( e[i].to ); fro.push( u );
}
}
}
int main()
{
scanf( "%lld%lld",&n,&s );
for ( ll i=1,u,v,w; i<n; i++ )
scanf( "%lld%lld%lld",&u,&v,&w ),add( u,v,w ),add( v,u,w );
get_path(); bfs();
pre[n+1]=r;
for ( ll i=n+1; i; i=pre[i] )
sum[pre[i]]=sum[i]+bet[i];
for ( ll L=r,R=r; L && R!=l; L=pre[L] )
{
ll las=R; h++;
while ( sum[R]-sum[L]<=s && R )
{
las=R; R=pre[R];
if ( R && sum[R]-sum[L]<=s )
{
while ( dis1[R]>=que[t] && t>=h ) t--;
que[++t]=dis1[R];
}
}
if ( R==0 || sum[R]-sum[L]>s ) R=las;
ll tmp=max( sum[L],sum[l]-sum[R] ); tmp=max( tmp,que[h] );
ans=min( tmp,ans );
}
printf( "%lld",ans );
return 0;
}
2——P2610 [ZJOI2012]旅遊
題意
T國的國土可以用一個凸N邊形來表示,包含 \(N-2\) 個城市,每個城市都是頂點為 \(N\) 邊形頂點的三角形,兩人的旅遊路線可以看做是連線N個頂點中不相鄰兩點的線段。問一路能經過最多多少城市。
一個城市被當做經過當且僅當其與線路有至少兩個公共點。
思路
很巧妙的一道題。(不愧是ZJOI)
三角剖分是個很有意思的資訊。不是讓你想遞推啊喂
考慮什麼樣的城市是不滿足三角剖分的。會發現,不可能存在一些城市圍成一圈(這樣有一個點就會在內部而不是端點),所以如果將相鄰的城市連邊,是不可能存在環的。沒有環,又是連通的,那麼就是樹了。
問題就轉化成了求樹的直徑,裸題。
然而事實上,建圖有億點麻煩...但是非常幸運的是,由於這道題是三角形,所以肯定是二叉樹,那麼我們有pair!我們有 map!我們有STL!重點是我們有O2!
於是這道題就做完了。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
struct edge
{
int to,nxt;
}e[N<<1];
int head[N],tot=0,n,s[N][3],ans,g[N],f[N];
map<pair<int,int>,int> mp;
void add( int u,int v )
{
e[++tot].to=v; e[tot].nxt=head[u]; head[u]=tot;
e[++tot].to=u; e[tot].nxt=head[v]; head[v]=tot;
}
int dfs( int u,int fa )
{
for ( int i=head[u]; i; i=e[i].nxt )
{
int v=e[i].to;
if ( v==fa ) continue;
int dis=dfs( v,u )+1;
if ( f[u]<dis ) g[u]=f[u],f[u]=dis;
else if ( g[u]<dis ) g[u]=dis;
}
ans=max( ans,f[u]+g[u]+1 );
return f[u];
}
void build( int i,int j,int u )
{
pair<int,int> pr=make_pair( i,j );
if ( mp[pr] ) add( u,mp[pr] );
else mp[pr]=u;
}
int main()
{
scanf( "%d",&n );
for ( int i=1; i<=n-2; i++ )
{
scanf( "%d%d%d",&s[i][0],&s[i][1],&s[i][2] ); sort( s[i],s[i]+3 );
build( s[i][0],s[i][1],i ); build( s[i][1],s[i][2],i ); build( s[i][0],s[i][2],i );
}
dfs( 1,0 );
printf( "%d",ans );
return 0;
}
3——P3629 [APIO2010]巡邏
題意
有 \(n\) 個村莊,編號為 \(1, 2, ..., n\) 。有 \(n – 1\) 條道路連線著這些村 莊,從任何一個村莊都可以到達其他任一個村莊。道路長度均為 1。 巡警車每天要到所有的道路上巡邏。警察局設在編號為 \(1\) 的村莊裡,每天巡警車總是從警察局出發又回到警察局。
在這些村莊之間建 \(K\) 條新的道路, 可以連線任意兩個村莊。每天巡警車必須 經過新建的道路正好一次. 求最小的巡邏距離。
思路
考慮逐條加邊。
如果不加邊,那麼答案顯然是 \(2(n-1)\).
如果加一條邊,由於必須經過恰好一次,所以在沿著新的道路 \((u,v)\) 走了一次之後,要返回 \(u\) ,必須沿著樹上的環的另一半再走一遍,那麼這時候 \(u\to v\) 的路徑只需要走一次,所以 \(ans=2(n-1)-L-+1.\)
再加一條邊,如果環沒有重疊,那麼按照一條的情況處理即可。否則,重疊部分不會被走過,所以還要走一次,又變成了需要走兩次的邊。
總結兩種情況,得到演算法:
- 找一遍直徑,邊權取反,長度為 \(L_1\)
- 再求直徑,得到 \(L_2\)
- \(ans=2(n-1)-(L_1-1)-(L_2-1)\)
程式碼
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct edge
{
int to,nxt,val;
}e[N<<1];
int n,k,tot=0,mx,head[N],dis[N],pre[N],f[N];
bool vis[N];
queue<int> q;
void add( int u,int v,int w )
{
e[++tot].to=v; e[tot].val=w; e[tot].nxt=head[u]; head[u]=tot;
}
int bfs( int s )
{
memset( dis,0x3f,sizeof(dis) );
q.push( s ); dis[s]=pre[s]=0;
while ( q.size() )
{
int t=q.front(); q.pop();
for ( int i=head[t]; i; i=e[i].nxt )
if ( dis[e[i].to]==0x3f3f3f3f )
dis[e[i].to]=dis[t]+e[i].val,pre[e[i].to]=i,q.push( e[i].to );
}
int res=1;
for ( int x=1; x<=n; x++ )
if ( dis[x]>dis[res] ) res=x;
return res;
}
void dp( int x )
{
vis[x]=1;
for ( int i=head[x]; i; i=e[i].nxt )
if ( !vis[e[i].to] )
{
dp( e[i].to );
mx=max( mx,f[e[i].to]+f[x]+e[i].val );
f[x]=max( f[x],f[e[i].to]+e[i].val );
}
}
int main()
{
memset( head,0,sizeof(head) ); tot=1;
scanf( "%d%d",&n,&k );
for ( int i=1,u,v; i<n; i++ )
scanf( "%d%d",&u,&v ),add( u,v,1 ),add( v,u,1 );
int l=bfs( 1 ); l=bfs(l);
int L1=dis[l],fl=1; mx=0;
if ( k==2 )
{
for ( ; pre[l]; l=e[pre[l]^1].to )
e[pre[l]].val=e[pre[l]^1].val=-1;
dp( 1 ); fl=2;
}
printf( "%d",2*(n-1)-L1-mx+fl );
return 0;
}
4——P4381 [IOI2008]Island
題意
\(N\) 個島嶼組成,從每個島嶼 \(i\) 出發向另外一個島嶼建了一座長度為 \(L_i\) 的橋,可以雙向行走。同時,每對島嶼之間都有一艘專用的往來兩島之間的渡船。你希望經過的橋的總長度儘可能長,但受到以下的限制:
- 可以自行挑選一個島開始遊覽。
- 任何一個島都不能遊覽一次以上。
- 任何時間都可以由當前所在的島 \(S\) 去另一個從未到過的島 \(D\)。從 \(S\) 到 \(D\) 有如下方法:
- 步行:僅當兩個島之間有一座橋,橋長會累加到步行總距離中。
- 渡船:僅當沒有任何橋和以前使用過的渡船的組合可以由 \(S\) 走到 \(D\) (檢查是否可到達時應該考慮所有路徑,包括經過曾遊覽過的島)。
注意,你不必遊覽所有的島,也可能無法走完所有的橋。
給定 \(N\) 座橋以及它們的長度,按照上述的規則,計算你可以走過的橋的長度之和的最大值。
思路
每個島嶼一座橋,\(n\) 個島嶼 \(n\) 座橋……誒,不是樹?出大問題
於是你不幸地發現,這是基環樹,而且還是基環樹森林!不過沒有關係。由題意可知,如果乘船離開一棵基環樹就不能再回來了。(因為回來要坐船,然而你坐過了,所以可達性成立,你就不能坐船了)
於是題目就變成了,求所有基環樹的直徑之和。
首先,找出基環樹的環。然後,對於直徑,顯然分成兩部分,要麼在去掉環的某棵子樹內,要麼是環上一段距離加上兩棵不同子樹內的距離。
於是可以先預處理出環上每個點在去掉環的前提下,子樹內的直徑。然後把環展開,分順時針和逆時針兩種情況討論,每次的 \(res=dis(i,j)+d[i]+d[j]\) ,取max 即可。
程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10;
int n,m,cnt=0,dfn[N],fa[N],pre[N],a[N<<1];
int head[N],tot=1,q[N<<1];
ll ans=0,d[N<<1],f[N],b[N<<1],res;
bool vis[N];
struct edge
{
int nxt,to,val;
}e[N<<1];
void add( int u,int v,int w )
{
e[++tot]=(edge){head[u],v,w}; head[u]=tot;
}
void bfs1( int u ) //找環
{
int tail=0; q[++tail]=u;
while ( tail )
{
int he=q[tail--]; dfn[he]=++cnt;
for ( int i=head[he]; i; i=e[i].nxt )
{
int v=e[i].to;
if ( i==(pre[he]^1) ) continue;
if ( !dfn[v] ) { fa[v]=he,pre[v]=i,q[++tail]=v; }
else if ( !m )
{
int p=he;
for ( ; p!=v; p=fa[p] )
a[++m]=p,d[m]=e[pre[p]].val,vis[p]=1;
a[++m]=v; d[m]=e[i].val; vis[v]=1;
}
}
}
}
ll bfs( int x )
{
int h=1,t=0; q[++t]=x;
while ( h<=t )
{
int u=q[h++];
for ( int i=head[u]; i; i=e[i].nxt )
{
int v=e[i].to;
if ( vis[v] ) continue;
fa[v]=u; vis[v]=1; q[++t]=v; pre[v]=e[i].val;
}
}
for ( int i=t; i>1; i-- )
{
int v=q[i],u=fa[v],w=pre[v];
res=max( res,f[u]+f[v]+w );
f[u]=max( f[u],f[v]+w );
}
return f[x];
}
void solve( int x )
{
m=res=0; bfs1( x );
if ( !m ) { bfs( x ); ans+=res; return; }
reverse( a+1,a+1+m ); reverse( d+1,d+1+m ); //反轉
for ( int i=1; i<=m; i++ )
b[i]=bfs( a[i] );
for ( int i=m+1; i<=m*2; i++ ) //複製,展環成鏈
a[i]=a[i-m],b[i]=b[i-m],d[i]=d[i-m];
for ( int i=1; i<=m*2; i++ )
d[i]+=d[i-1];
int h=0,t=0; q[0]=0;
for ( int i=1; i<=m*2; i++ )
{
while ( h<=t && i-q[h]+1>m ) h++;
if ( h<=t ) res=max( res,b[i]+b[q[h]]+d[i]-d[q[h]] );
while ( h<=t && b[q[t]]-d[q[t]]<=b[i]-d[i] ) t--;
q[++t]=i;
}
ans+=res;
}
int main()
{
scanf( "%d",&n );
for ( int i=1,v,w; i<=n; i++ )
scanf( "%d%d",&v,&w ),add( v,i,w ),add( i,v,w );
for ( int i=1; i<=n; i++ )
if ( !dfn[i] ) solve( i );
printf( "%lld\n",ans );
}
以上是樹的直徑部分。
5——P5021 賽道修建
題意
有一棵帶邊權的樹,在樹上選出 \(m\) 條互不相交的鏈(點可以重合,但是邊不能重合),使得 \(m\) 條鏈中最短的鏈最長。
思路
看到這個很容易想到總體思路是二分。
考慮如何判定。然後發現這道題其實藏了一個貪心:對於一棵子樹內的所有鏈,在有最多的兒子對答案做出貢獻的前提下,最大化 \(f_i\) 的值。由於 一個點的 \(f\) 最多對答案產生 \(1\) 的貢獻,所以讓兒子更少貢獻,轉移更大的 \(f_i\) 不會變優。
那麼正解就來了。首先二分出一個 \(mid\) 。然後對於一棵子樹,令 \(f_i\) 為以 \(i\) 為根的子樹中,最優的不完整的鏈長(完整的根據上面的貪心分析,已經 \(\ge mid\) ,貢獻到答案裡去了;不完整就是還要和別的鏈拼接的)。
暫時不考慮根節點 \(i\) ,對於 \(i\) 所有的兒子,如果能單獨貢獻則直接計入答案。否則,嘗試兩兩合併這些子鏈(見題意,點是可以重合的,在根節點合併多少都沒有關係),如果長度 \(\ge mid\) 就計入答案。如果都不行,那就在這些剩餘的鏈中選取最長的一條,計入 \(f_i\) ,嘗試往上走。於是就可以把所有子節點的 \(f_j\) 排序,貪心找最大的匹配數(能匹配的幾條中最小的一個),然後把剩下的轉移給 \(f_i\) 即可。
程式碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+10;
struct edge
{
int to,nxt; ll val;
}e[N<<1];
int head[N],tot=0,n,m;
ll f[N],subans[N];
vector<int> son[N];
void add( int u,int v,ll w )
{
e[++tot].to=v; e[tot].nxt=head[u]; e[tot].val=w; head[u]=tot;
}
int get_ans( int u,int pos,int cnt,ll x )
{
int res=0,l=0;
for ( int r=cnt-1; r; r-- )
{
r-=(r==pos);
while ( l<r && son[u][l]+son[u][r]<x ) ++l;
l+=(l==pos);
if ( l>=r ) break;
res++; l++;
}
return res;
}
void dfs( int u,int fa,ll x )
{
f[u]=subans[u]=0; son[u].clear();
for ( int i=head[u]; i; i=e[i].nxt )
{
int v=e[i].to;
if ( v==fa ) continue;
dfs( v,u,x ); f[v]+=e[i].val;
if ( f[v]>=x ) subans[u]++;
else son[u].push_back( f[v] );
}
int cnt=son[u].size(); sort( son[u].begin(),son[u].end() );
int l=0,r=cnt,sub=0,res;
for ( int r=cnt-1; r; r-- ) //配對
{
while ( l<r && son[u][l]+son[u][r]<x ) l++;
if ( l>=r ) break;
sub++; l++;
}
subans[u]+=sub;
if ( sub*2==cnt ) return;
l=0; r=cnt-1;
while ( l<=r ) //二分找最大的一個mid,使得首先滿足子樹要求
{
int mid=(l+r)>>1;
int tmp=get_ans( u,mid,cnt,x );
if ( tmp==sub ) res=mid,l=mid+1;
else r=mid-1;
}
f[u]=son[u][res]; //記入f[i]
}
bool check( ll x )
{
int res=0; dfs( 1,0,x );
for ( int i=1; i<=n; i++ ) //每個點的貢獻
res+=subans[i];
return res>=m;
}
int main()
{
ll l=0,r=0;
scanf( "%d%d",&n,&m );
for ( int i=1; i<n; i++ )
{
int u,v; ll w; scanf( "%d%d%lld",&u,&v,&w );
add( u,v,w ); add( v,u,w ); r+=w;
}
r/=(ll)m; ll ans=0;
while ( l<=r )
{
ll mid=(l+r)>>1;
if ( check(mid) ) ans=mid,l=mid+1;
else r=mid-1;
}
printf( "%lld",ans );
return 0;
}
6——P1852 跳跳棋
題意
跳跳棋是在一條數軸上進行的。棋子只能擺在整點上。每個點不能擺超過一個棋子。棋盤上有3顆棋子,分別在 \(a,b,c\) 這三個位置。我們要通過最少的跳動把他們的位置移動成 \(x,y,z\) 。(棋子是沒有區別的)
跳動的規則很簡單,任意選一顆棋子,對一顆中軸棋子跳動。跳動後兩顆棋子距離不變。一次只允許跳過1顆棋子。
判斷是否可以完成任務。如果可以,輸出最少需要的跳動次數。
思路
神仙題……非常巧妙地建模。只能說:女少口阿
首先,對於中軸棋子為 \(b\) (中間那個)的情況,顯然一直往中間跳可以一直減小範圍,直到不能跳為止。這時候就得到了一個非常有用的“Basic” 狀態,也就是“根狀態”(這怎麼跟某道字串手玩題這麼像啊)
然後把 \(b\) 往左右跳的情況看成左右節點狀態,那麼所有狀態構成了一棵二叉樹。對於棋盤上所有的 \(a,b,c\) ,狀態構成了一個森林。
那麼,如果 \((a,b,c)\to (x,y,z)\) ,首要條件是在同一棵樹上。這樣第一問就解決了。
考慮狀態怎麼去樹根。利用 LCA 的思想,把兩個狀態到根的距離調整到一樣,然後二分向上的步數,最後找到一個 \(L\) 使得兩個狀態向上 \(L\) 步相遇,那麼總答案就是 高度差加上二分答案的兩倍。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int inf=1e9+7;
int sx,sy,sz,dep,mx;
void init( int &x,int &y,int &z )
{
x+=inf; y+=inf; z+=inf;
if ( y>z ) swap( y,z );
if ( x>y ) swap( x,y );
if ( y>z ) swap( y,z );
}
void dfs( int x,int y,int z,int step )
{
int del1=y-x,del2=z-y;
if ( step==mx || del1==del2 ) { sx=x,sy=y,sz=z; dep=step; return; }
if ( del1>del2 )
{
swap( del1,del2 ); int del=del2/del1;
if ( del2%del1==0 ) del--;
if ( step+del<=mx ) dfs( x,y-del*del1,z-del*del1,step+del );
else dfs( x,y-(mx-step)*del1,z-(mx-step)*del1,mx );
}
else
{
int del=del2/del1; del-=(del2%del1==0);
if ( step+del<=mx ) dfs( x+del*del1,y+del*del1,z,step+del );
else dfs( x+(mx-step)*del1,y+(mx-step)*del1,z,mx );
}
}
int main()
{
int x,y,z,a,b,c;
scanf( "%d%d%d",&a,&b,&c ); init( a,b,c );
scanf( "%d%d%d",&x,&y,&z ); init( x,y,z );
mx=inf;
dfs( a,b,c,0 ); int sa=sx,sb=sy,sc=sz,sd=dep;
dfs( x,y,z,0 );
if ( sx!=sa || sy!=sb || sz!=sc ) { printf( "NO" ); return 0; }
printf( "YES\n" );
//------------query1-------------------
int ans=0;
if ( sd>dep )
{
ans=sd-dep; mx=sd-dep;
dfs( a,b,c,0 ); a=sx; b=sy; c=sz;
}
if ( sd<dep )
{
ans=dep-sd; mx=dep-sd;
dfs( x,y,z,0 ); x=sx,y=sy,z=sz;
}
int l=0,r=inf;
while ( l<=r )
{
mx=(l+r)>>1;
dfs( a,b,c,0 ); sa=sx,sb=sy,sc=sz;
dfs( x,y,z,0 );
if ( sa!=sx || sb!=sy || sc!=sz ) l=mx+1;
else r=mx-1;
}
printf( "%d",(l<<1)+ans );
}
Last
To be continue...