[LDUoj 倍增] 题解
Posted PushyTao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LDUoj 倍增] 题解相关的知识,希望对你有一定的参考价值。
星星之火,可以燎原
细节的地方慢慢补充,欢迎提出问题,私聊/留言均可
A. 跳跳棋
较难
struct node
{
int a, b, c;
friend bool operator != (node a, node b)
{
if(a.a != b.a || a.b != b.b || a.c != b.c) return true;
return false;
}
void srt()
{
int temp[] = {a, b, c};
sort(temp, temp + 3);
a = temp[0], b = temp[1], c = temp[2];
}
} pnt1, pnt2;
node getRoot(node pnt, int &cnt)
{
/// dis1 == dis2 -> return pnt ok;
while(pnt.c - pnt.b != pnt.b - pnt.a)
{
int dis1 = pnt.b - pnt.a;
int dis2 = pnt.c - pnt.b;
if(dis1 < dis2)
{
int t = dis2 / dis1;
if(dis2 % dis1 == 0) -- t;
pnt.a += t * dis1;
pnt.b += t * dis1;
cnt += t;
}
else
{
int t = dis1 / dis2;
if(dis1 % dis2 == 0) -- t;
pnt.c -= t * dis2;
pnt.b -= t * dis2;
cnt += t;
}
}
return pnt;
}
node moveUp(node pnt, int cnt)
{
while(pnt.b - pnt.a != pnt.c - pnt.b && cnt)
{
int dis1 = pnt.b - pnt.a;
int dis2 = pnt.c - pnt.b;
if(dis1 < dis2)
{
int t = dis2 / dis1;
if(dis2 % dis1 == 0) -- t;
if(cnt < t) t = cnt;
pnt.a += t * dis1;
pnt.b += t * dis1;
cnt -= t;
}
else
{
int t = dis1 / dis2;
if(dis1 % dis2 == 0) -- t;
if(cnt < t) t = cnt;
pnt.c -= t * dis2;
pnt.b -= t * dis2;
cnt -= t;
}
}
return pnt;
}
int main()
{
cin >> pnt1.a >> pnt1.b >> pnt1.c;
cin >> pnt2.a >> pnt2.b >> pnt2.c;
pnt1.srt(), pnt2.srt();
int cnt1 = 0, cnt2 = 0;
node root1 = getRoot(pnt1, cnt1);
node root2 = getRoot(pnt2, cnt2);
if(root1 != root2)
{
puts("NO");
return 0;
}
// puts("OK");
if(cnt1 < cnt2) swap(pnt1, pnt2), swap(cnt1, cnt2);
int l = 0, r = 1000000000;
pnt1 = moveUp(pnt1, cnt1 - cnt2);
int ans = 0;
// puts("OK");
while(l <= r)
{
int mid = l + r >> 1;
// cout << l << " " << r <<endl;
if(!(moveUp(pnt1, mid) != moveUp(pnt2, mid)))
{
r = mid - 1;
ans = mid;
}
else l = mid + 1;
// puts("end of while");
}
puts("YES");
cout << ans * 2 + cnt1 - cnt2 << endl;
return 0;
}
/**
**/
B. 聚会
板子题
n个城市之间的距离是1
所以在求lca的过程中,深度的差值即可表示为距离
两个点a,b
,之间的lca为_lca
那么这两个点之间的距离为dep[a] - dep[_lca] + dep[b] - dep[_lca]
,-> dep[a] + dep[b] - dep[_lca] * 2
这仅仅是两个点的情况
三个点的情况略有不同:
若三个点在同一棵子树上:
若三个点在同一条链上:
所以说我们要考虑任意两点之间的关系,然后求两次lca,第一次的lca即为可能的答案位置,最终答案要考虑所有情况中花费最小的那个
int n,m;
int root;
int head[maxn];
struct node {
int u;
int v;
int w;
int next;
} e[maxn];
int dep[maxn];/// save the depth of every node
int fa[maxn][30];
int lg[maxn];
int cnt = 0;
void init() {
memset(head,-1,sizeof head);
}
void add(int u,int v) {
e[cnt].u = u;
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt ++;
}
void dfs(int cur,int rt) {
fa[cur][0] = rt,dep[cur] = dep[rt] + 1;/// the depth of the current node changed
for(int i=1; i <= lg[dep[cur]]; i++) {
fa[cur][i] = fa[fa[cur][i-1]][i-1];
}
for(int i=head[cur]; ~i; i = e[i].next) { /// visit all linked nodes
if(e[i].v != rt) dfs(e[i].v,cur);
}
}
void cal() {
for(int i=1; i<=n; i++) {
lg[i] = lg[i-1] + (1 << lg[i-1] == i);/// 2 ^ lg[i-1] == i true + 1
}
}
int lca(int x,int y) {
if(dep[x] < dep[y]) swap(x,y);
while(dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
if(x == y) return y;
/// big -> small
for(int k = lg[dep[x]] - 1; k >= 0; k --) {
if(fa[x][k] != fa[y][k]) {
x = fa[x][k];
y = fa[y][k];
}
}
return fa[x][0];
}
int main() {
cin >> n >> m;
cal();
init();
for(int i=1; i<n; i++) {
int u = read,v = read;
add(u,v);
add(v,u);
}
ll ans = inf,dis;
int pos = 0;
int lca1,lca2;
dfs(1,0);
for(int i=1; i<=m; i++) {
ans = inf;
int x = read,y = read,z = read;
lca1 = lca(x,y);
dis = dep[x] + dep[y] - 2 * dep[lca1];
lca2 = lca(lca1,z);
dis += dep[lca1] + dep[z] - 2 * dep[lca2];
if(dis < ans) {
ans = dis;
pos = lca1;
}
// debug(lca1);
// debug(dis);
swap(x,z);
lca1 = lca(x,y);
dis = dep[x] + dep[y] - 2 * dep[lca1];
lca2 = lca(lca1,z);
dis += dep[lca1] + dep[z] - 2 * dep[lca2];
if(dis < ans) {
ans = dis;
pos = lca1;
}
swap(y,z);
lca1 = lca(x,y);
dis = dep[x] + dep[y] - 2 * dep[lca1];
lca2 = lca(lca1,z);
dis += dep[lca1] + dep[z] - 2 * dep[lca2];
if(dis <ans) {
ans = dis;
pos = lca1;
}
printf("%d %lld\\n",pos,ans);
}
return 0;
}
/**
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
6 2
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
**/
C. 祖孙询问
板子题
求两点之间的lca,如果lca与其中一点相同,输出对应的值,反之输出0
int n,m;
int root;
int head[maxn];
struct node{
int u;
int v;
int next;
}e[maxn];
int dep[maxn];/// save the depth of every node
int fa[maxn][30];
int lg[maxn];
int cnt = 0;
void init(){
memset(head,-1,sizeof head);
}
void add(int u,int v){
e[cnt].u = u;
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt ++;
}
void dfs(int cur,int rt){
fa[cur][0] = rt,dep[cur] = dep[rt] + 1;/// the depth of the current node changed
for(int i=1;i <= lg[dep[cur]];i++){
fa[cur][i] = fa[fa[cur][i-1]][i-1];
}
for(int i=head[cur];~i;i = e[i].next){/// visit all linked nodes
if(e[i].v != rt) dfs(e[i].v,cur);
}
}
void cal(){
for(int i=1;i<=n;i++){
lg[i] = lg[i-1] + (1 << lg[i-1] == i);/// 2 ^ lg[i-1] == i true + 1
}
}
int lca(int x,int y){
if(dep[x] < dep[y]) swap(x,y);
while(dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1];
if(x == y) return y;
/// big -> small
for(int k = lg[dep[x]] - 1;k >= 0;k --){
if(fa[x][k] != fa[y][k]){
x = fa[x][k];
y = fa[y][k];
}
}
return fa[x][0];
}
int main() {
cin >> n;
cal();
init();
for(int i=1;i<=n;i++){
int x = read,y = read;
if(y == -1) {
root = x;
continue;
}
add(x,y);
add(y,x);
}
dfs(root,0);
cin >> m;
for(int i=1;i<=m;i++){
int x = read,y = read;
int _lca = lca(x,y);
if(_lca == x) puts("1");
else if(_lca == y) puts("2");
else puts("0");
}
return 0;
}/// ac_code
/**
**/
D. Dis
板子题
考虑在更新depth的时候,顺便更新一下两点之间的距离,(在题目中如果说任意两点之间的距离为1的情况,dis可以用depth代替)
typedef pair<int,int> PII;
int n,m;
int root题解——dinner(二分+倍增)
Codeforces 827D Best Edge Weight 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)