生活随笔
收集整理的這篇文章主要介紹了
牛客练习赛 67——ST表
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
A.牛牛愛字符串
注意原字符串有空格,不要用cin
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std
;
int main()
{IO
;int T
=1;while(T
--){string s
;while(getline(cin
,s
)){vector
<string
> ans
;int n
=s
.size();for(int i
=0;i
<n
;i
++){if(s
[i
]<'0'||s
[i
]>'9') continue;string now
="";while(i
<n
&&s
[i
]>='0'&&s
[i
]<='9'){now
+=s
[i
];i
++;}reverse(now
.begin(),now
.end());while(now
.size()>1&&now
.back()=='0') now
.pop_back();reverse(now
.begin(),now
.end());ans
.push_back(now
);} for(auto t
:ans
) cout
<<t
<<' ';cout
<<'\n';}}return 0;
}
B.牛牛愛位運算
&只會越運算越小,直接選擇最大的數即可
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std
;
const int N
=100010;
int a
[N
],n
;
int main()
{IO
;int T
=1;cin
>>T
;while(T
--){cin
>>n
;for(int i
=1;i
<=n
;i
++) cin
>>a
[i
];sort(a
+1,a
+1+n
);cout
<<a
[n
]<<'\n';}return 0;
}
C.牛牛愛博弈
打表找規律,懂得都懂
最終只要nnn不是333的倍數先手必勝
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
using namespace std
;
const int N
=110;
int sg
[N
];
int main()
{IO
;int T
=1;cin
>>T
;memset(sg
,-1,sizeof sg
);while(T
--){cin
>>n
;if(n
%3) cout
<<"Alan\n";else cout
<<"Frame\n";}return 0;
}
D. 牛妹愛數列
f[i][0/1]f[i][0/1]f[i][0/1]表示:考慮前iii個序列,把他們全變成0/10/10/1的最小翻轉次數
轉移:考慮最后一步是單點修改還是前綴修改。
如果是單點修改f[i][1]=min(f[i][1],f[i?1][1]+1?a[i]),f[i][0]=min(f[i][0],f[i?1][0]+a[i])f[i][1]=min(f[i][1],f[i-1][1]+1-a[i]),f[i][0]=min(f[i][0],f[i-1][0]+a[i])f[i][1]=min(f[i][1],f[i?1][1]+1?a[i]),f[i][0]=min(f[i][0],f[i?1][0]+a[i])
如果是前綴修改f[i][1]=min(f[i][1],f[i?1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i?1][1]+1?a[i]+1)f[i][1]=min(f[i][1],f[i-1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i-1][1]+1-a[i]+1)f[i][1]=min(f[i][1],f[i?1][0]+a[i]+1),f[i][0]=min(f[i][0],f[i?1][1]+1?a[i]+1)
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std
;
const int N
=100010;
int f
[N
][2];
int a
[N
],n
;
int main()
{IO
;int T
=1;while(T
--){cin
>>n
;for(int i
=1;i
<=n
;i
++) cin
>>a
[i
];memset(f
,0x3f,sizeof f
);f
[0][0]=f
[0][1]=0;for(int i
=1;i
<=n
;i
++){f
[i
][0]=min(f
[i
-1][0]+a
[i
],f
[i
-1][1]+1-a
[i
]+1);f
[i
][1]=min(f
[i
-1][0]+1-a
[i
]+1,f
[i
-1][1]+1-a
[i
]);}cout
<<f
[n
][0]<<'\n';}return 0;
}
E.牛妹游歷城市
暴力建圖邊數達到恐怖的n2n^2n2不光空間開不下,而且時間跑不過,不可取。
考慮優化建圖:建立323232個虛擬源點(編號n+1…n+32)n+1 \dots n+32)n+1…n+32)如果一個點iii的aia_iai?的二進制表示中第j(0≤j≤31)j(0\leq j\leq 31)j(0≤j≤31)位是1,那么連一條邊i?>n+j+1i->n+j+1i?>n+j+1權值是1<<j1<<j1<<j的邊,并且連一條n+j+1?>in+j+1->in+j+1?>i權值是000的邊。然后不難發現原圖中的所有邊都可以轉化成新圖的邊,同樣新圖的所有邊都能轉化成原圖的邊。 但是并不影響求最短路比如權值是101011和101010的兩個點,我們會建立邊權是10、1000、100000這些路徑,但是發現這些相當于重邊,不會影響答案。
博主的圖非常清楚直接秒懂
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std
;
typedef long long ll
;
typedef pair
<ll
,int> pli
;
const int N
=111010,M
=1000010;
const ll INF
=0x3f3f3f3f3f3f3f3f;
int h
[N
],e
[M
],ne
[M
],idx
;
ll w
[M
],a
[N
];
ll dist
[N
];
bool st
[N
];
int n
;
void add(int a
,int b
,ll c
)
{e
[idx
]=b
;w
[idx
]=c
;ne
[idx
]=h
[a
];h
[a
]=idx
++;
}
void dijkstra()
{memset(dist
,0x3f,sizeof dist
);memset(st
,0,sizeof st
);dist
[1]=0;priority_queue
<pli
,vector
<pli
>,greater
<pli
>> q
;q
.push({0,1});while(q
.size()){int t
=q
.top().second
;q
.pop();if(st
[t
]) continue;st
[t
]=1;for(int i
=h
[t
];i
!=-1;i
=ne
[i
]){int j
=e
[i
];if(dist
[j
]>dist
[t
]+w
[i
]){dist
[j
]=dist
[t
]+w
[i
];q
.push({dist
[j
],j
});}}}
}
int main()
{IO
;int T
=1;cin
>>T
;while(T
--){cin
>>n
;memset(h
,-1,sizeof h
);idx
=0;for(int i
=1;i
<=n
;i
++) cin
>>a
[i
];for(int i
=1;i
<=n
;i
++)for(int j
=0;j
<32;j
++)if(a
[i
]>>j
&1){add(i
,j
+n
+1,1ll<<j
);add(j
+n
+1,i
,0ll);}dijkstra();if(dist
[n
]==INF
) cout
<<"Impossible\n";else cout
<<dist
[n
]<<'\n';}return 0;
}
學習ST表,F明天補了
F.牛妹的蘋果樹
方法一:線段樹(動態)+歐拉序ST表求lca
首先有一個引理:樹的直徑具有可合并的性質,同一棵樹上兩個區間的直徑的兩個端點分別為 (a,b)(a,b)(a,b),(c,d)(c,d)(c,d),那么合并兩個區間后的新的直徑的端點一定在a,b,c,d{a,b,c,d}a,b,c,d中,通過枚舉端點計算它們的距離(6種情況),取最大值可以得到兩個區間合并的直徑。 其正確性證明和兩遍dfs求樹的直徑的證明過程類似。
由此我們考慮線段樹維護區間直徑即可。求lca最好用歐拉序+ST表的,這個我不會可以借鑒大佬寫法線段樹+ST表求lca
方法二:ST表維護直徑(靜態)+ST表求lca
由于本題為區間靜態問題,并且直徑有區間可重復計算的性質,考慮用ST表維護直徑ST表維護直徑
#include<iostream>
#include<algorithm>
#include<cstring>
#define x first
#define y second
using namespace std
;
typedef long long ll
;
typedef pair
<int,int> pii
;
const int N
=300010,M
=2*N
;
int h
[N
],e
[M
],ne
[M
],idx
;
ll w
[M
];
ll dist
[N
];
int dfn
[N
],eular
[M
],ST
[M
][20],cnt
;
int lg
[M
];
pii f
[N
][20];
int n
,q
;
void add(int a
,int b
,ll c
)
{e
[idx
]=b
;w
[idx
]=c
;ne
[idx
]=h
[a
];h
[a
]=idx
++;
}void dfs(int u
,int fa
)
{dfn
[u
]=++cnt
;ST
[cnt
][0]=u
;f
[u
][0]={u
,u
};for(int i
=h
[u
];i
!=-1;i
=ne
[i
]){int j
=e
[i
];if(j
==fa
) continue;dist
[j
]=dist
[u
]+w
[i
];dfs(j
,u
);ST
[++cnt
][0]=u
;}
}
int lca(int a
,int b
)
{a
=dfn
[a
],b
=dfn
[b
];if(a
>b
) swap(a
,b
);int len
=lg
[b
-a
+1];return dist
[ST
[a
][len
]]<dist
[ST
[b
-(1<<len
)+1][len
]]?ST
[a
][len
]:ST
[b
-(1<<len
)+1][len
];
}
ll
calc(int a
,int b
)
{int pab
=lca(a
,b
);return dist
[a
]+dist
[b
]-2*dist
[pab
];
}
void pre()
{for(int i
=2;i
<=cnt
;i
++) lg
[i
]=lg
[i
>>1]+1;for(int j
=1;j
<=lg
[cnt
];j
++)for(int i
=1;i
+(1<<j
)-1<=cnt
;i
++)ST
[i
][j
]=dist
[ST
[i
][j
-1]]<dist
[ST
[i
+(1<<j
-1)][j
-1]]?ST
[i
][j
-1]:ST
[i
+(1<<j
-1)][j
-1];for(int j
=1;j
<=lg
[n
];j
++)for(int i
=1;i
+(1<<j
)-1<=n
;i
++){pii a
=f
[i
][j
-1],b
=f
[i
+(1<<j
-1)][j
-1];f
[i
][j
]={a
.x
,a
.y
};if(calc(a
.x
,b
.x
)>calc(f
[i
][j
].x
,f
[i
][j
].y
)) f
[i
][j
]={a
.x
,b
.x
};if(calc(a
.x
,b
.y
)>calc(f
[i
][j
].x
,f
[i
][j
].y
)) f
[i
][j
]={a
.x
,b
.y
};if(calc(a
.y
,b
.x
)>calc(f
[i
][j
].x
,f
[i
][j
].y
)) f
[i
][j
]={a
.y
,b
.x
};if(calc(a
.y
,b
.y
)>calc(f
[i
][j
].x
,f
[i
][j
].y
)) f
[i
][j
]={a
.y
,b
.y
};if(calc(b
.x
,b
.y
)>calc(f
[i
][j
].x
,f
[i
][j
].y
)) f
[i
][j
]={b
.x
,b
.y
};}
}
ll
query(int l
,int r
)
{int len
=lg
[r
-l
+1];pii a
=f
[l
][len
],b
=f
[r
-(1<<len
)+1][len
];ll res
=calc(a
.x
,a
.y
);res
=max(res
,calc(a
.x
,b
.x
));res
=max(res
,calc(a
.x
,b
.y
));res
=max(res
,calc(a
.y
,b
.x
));res
=max(res
,calc(a
.y
,b
.y
));res
=max(res
,calc(b
.x
,b
.y
));return res
;
}
int main()
{memset(h
,-1,sizeof h
);cin
>>n
>>q
;for(int i
=1;i
<n
;i
++){int a
,b
;ll c
;cin
>>a
>>b
>>c
;add(a
,b
,c
),add(b
,a
,c
);}dfs(1,0);pre();while(q
--){int l
,r
;cin
>>l
>>r
;cout
<<query(l
,r
)<<'\n';}return 0;
}
總結
以上是生活随笔為你收集整理的牛客练习赛 67——ST表的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。