cf593d

题意:给出一个有n(n<=200000)的树形图,每条边有一个权值。有两种操作,1是将一个边的权值变小,

2是给定两点a,b和一个值y,用y(long long范围内)依次除以两点之间的路径上的所有边的权值,每次除法都是向下取整。

并输出这个值。

操作次数为m(m<=200000)。

 

分析:

这是一个较难的在线LCA问题。

首先,每个y最多经过63次>=2的除数的除法就会变为0。

建树并生成LCA。

每次处理第2种请求时,我们都是先求两点的LCA,再分别处理两点到其LCA的路径。

对于从低到高走路径的过程中,连续的权值为1的边,我们用一种类似于并查集的方式将他们缩成一条边。

例如:a是b的父亲,边权值为1。那么我们领link[b]=a。否则我们令link[b]=b。

这样如果出现连续的权值为1的边,我们不断追溯其link值直到与自身相等即可。记得使用并查集的路径压缩优化。

那么我们处理单个路径时最多需要处理的边数约为63×2(权值>=2的和=1的边交替出现的情况)。

所以总复杂度为O(mlogy)

 

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;

#define d(x) 
const int MAX_NODE_NUM  = (int)(2e5) + 100;
const int M = 30;

int n, m;
vector<pair<int, long long> > edge[MAX_NODE_NUM];
bool vis[MAX_NODE_NUM];
int father[MAX_NODE_NUM][M];
int depth[MAX_NODE_NUM];
long long value[MAX_NODE_NUM];
pair<int, int> p_edge[MAX_NODE_NUM];
int link[MAX_NODE_NUM];

void bfs(int root)
{
    queue<int> q;
    q.push(root);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = true;
        for (int i = 0; i < (int)edge[u].size(); i++)
        {
            int v = edge[u][i].first;
            if (vis[v])
            {
                continue;
            }
            father[v][0] = u;
            depth[v] = depth[u] + 1;
            value[v] = edge[u][i].second;
            link[v] = v;
            if (u != 0 && value[v] == 1)
                link[v] = u;
            q.push(v);
        }
    }
}

//index start from 1.
void init_LCA(int root)
{
    fill_n(vis, n + 1, 0);
    memset(father, 0, sizeof(father));
    bfs(root);
    bool did;
    for (int i = 1; i < M; i++)
    {
        did = false;
        for (int j = 1; j <= n; j++)
        {
            int k = father[j][i - 1];
            if (k <= 0)
            {
                continue;
            }
            father[j][i] = father[k][i - 1];
            did = true;
        }
        if (!did)
        {
            break;
        }
    }
}

//O(log(n))
int LCA(int x, int y)
{
    if (depth[x] > depth[y])
    {
        swap(x, y);
    }
    int diff = depth[y] - depth[x];
    for (int i = 0; i < M && diff; i++)
    {
        if (diff & 1)
        {
            y = father[y][i];
        }
        diff >>= 1;
    }
    if (x == y)
    {
        return x;
    }
    int exp = 0;
    while (x != y)
    {
        if (!exp || father[x][exp] != father[y][exp])
        {
            x = father[x][exp];
            y = father[y][exp];
            exp++;
        }else
        {
            exp--;
        }
    }
    return x;
}

long long next_ll()
{
    long long a;
    scanf("%lld", &a);
    return a;
}

void input()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n - 1; i++)
    {
        int a, b;
        long long c;

        scanf("%d%d", &a, &b);
        c = next_ll();
        edge[a].push_back(make_pair(b, c));
        edge[b].push_back(make_pair(a, c));
        p_edge[i] = make_pair(a, b);
    }
}

int get_link(int a)
{
    if (a == link[a])
        return a;
    return link[a] = get_link(link[a]);
}

void make(int a, int lca, long long &w)
{
    while (w > 0 && depth[a] > depth[lca])
    {
        w /= value[a];
        a = father[a][0];
        a = get_link(a);
    }
}

void work(int a, int b, long long w)
{
    int lca = LCA(a, b);
    make(a, lca, w);
    make(b, lca, w);
    printf("%lld\n", w);
}

void work(int p, long long w)
{
    p--;
    int u = p_edge[p].first;
    int v = p_edge[p].second;
    if (depth[u] > depth[v])
    {
        swap(u, v);
    }
    value[v]= w;
    if (w == 1)
    {
        link[v] = link[u];
    }
}

int main()
{
    input();
    init_LCA(1);
    for (int i = 0; i < m; i++)
    {
        int command;
        int a, b;
        long long c;
        scanf("%d", &command);
        if (command == 1)
        {
            scanf("%d%d", &a, &b);
            c = next_ll();
            work(a, b, c);
        }else
        {
            scanf("%d", &a);
            c = next_ll();
            work(a, c);
        }
    }
    return 0;
}
View Code

 

posted @ 2015-12-27 12:42  金海峰  阅读(950)  评论(0编辑  收藏  举报