[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 倍增 + 并查集 || 倍增 + 压倍增标记 (看题解)

题解Luogu P1613 跑路 倍增+最短路

倍增选做-

题解Luogu P3509 [POI 2010] ZAB-Frog 倍增dp

POJ1679 The Unique MST 题解 次小生成树 题解 Kruskal+暴力LCA解法(因为倍增不会写)