LCA练习题

poj1330

题意:求祖先,解释了啥是LCA。
想法:模板。

#include
#include
#include
#include
using namespace std;
#define M 100009
int f[M],in[M],ancestor[M];
vector<int> tree[M],query[M];
int t,n,a,b,root,qa,qb;
bool vis[M];
int find(int x)
{
    return x == f[x] ? f[x] : f[x] = find(f[x]);
}
int uni(int x,int y)
{
    x = find(x);
    y = find(y);
    if(x != y) f[x] = y;
}
void init()
{
    for(int i = 0; i <= n; i++)
    {
        vis[i] = false;
        tree[i].clear();
        query[i].clear();
        in[i] = 0;
        f[i] = i;
    }
}
void tarjan(int x)
{
    for(int i = 0; i < tree[x].size(); i++)
    {
        int v = tree[x][i];
        tarjan(v);  //先处理子树
        uni(x,v); //将儿子节点的集合和父亲节点的集合合并
        ancestor[find(x)] = x; //将该节点所在集合的代表的祖先设置为x
    }
    vis[x] = true;
    for(int i = 0; i < query[x].size(); i++)
    {
        int v = query[x][i];
        if(vis[v])
        {
            printf("%d\n",ancestor[find(v)]);
        }
    }
}
int main()
{
    while(scanf("%d",&t) == 1)
    {
        while(t--)
        {
            scanf("%d",&n);
            init();
            for(int i = 0; i < n - 1; i++)
            {
                int a,b;
                scanf("%d %d",&a,&b);
                tree[a].push_back(b);
                in[b]++;
            }
            scanf("%d %d",&qa,&qb);
            query[qa].push_back(qb);
            query[qb].push_back(qa);
            for(int i = 1; i <= n; i++)
                if(in[i] == 0) root = i;

            tarjan(root);//从根节点开始tarjan
        }
    }
    return 0;
}

hdu2586 (存模板XD)
题意:给你一些小屋之间的距离,问任意小屋之间的距离。
想法:用lca来求祖先,用祖先之间的距离进行转换

#include
using namespace std;
const int maxn=55000;
vector<int>v[maxn],w[maxn],query[maxn],num[maxn];
///v表示连接的点,w表示连接之间的距离,query表示询问谁与谁之间,num表示询问的编号,利于最后直接输出是第几个询问的结果
int f[maxn],dis[maxn],ans[maxn];///dis表示根到这点的距离
bool vis[maxn];
int n;
void init()
{
    for(int i=1;i<=n;++i)
    {
        v[i].clear();
        w[i].clear();
        query[i].clear();
        num[i].clear();
        f[i]=i;
        dis[i]=0;
        vis[i]=false;///n个地方起初全都未遍历
    }
}
int Find(int x)
{
    if(f[x]==x)
        return x;
    else
       return Find(f[x]);

}
void join(int x,int y)
{
    int t1 = Find(x);
    int t2= Find(y);
    if(t1!=t2)
        f[t2]=t1;
    //if(x == y) return;
    //f[y] = x;
}
void tarjan(int cur,int val)///当前的点 根到这个点的距离
{
    vis[cur]=true;
    dis[cur]=val;
    int size=v[cur].size();///当前以此点为根的集合的大小
    for(int i=0;iint tmp=v[cur][i];///当前根与它集合内的这些点
        if(vis[tmp])///这个点已经访问过了 就去看下一个点
            continue;
        else///如果没被访问过
            tarjan(tmp,val+w[cur][i]);///没被访问的这个点就要当做根
        join(cur,tmp);///然后把这两个集合合并
    }
    int len=query[cur].size();
    for(int i=0;iint tmp=query[cur][i];///表示cur与其集合内的第几个点进行询问
        if(!vis[tmp])
            continue;
        ans[num[cur][i]]=dis[cur]+dis[tmp]-2*dis[Find(tmp)];
    }
}
int main()
{
    int T,Q,x,y,z;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&Q);
        init();
        for(int i=1;iscanf("%d%d%d",&x,&y,&z);
            v[x].push_back(y);
            w[x].push_back(z);
            v[y].push_back(x);
            w[y].push_back(z);
        }
        for(int i=0;iscanf("%d%d",&x,&y);
            query[x].push_back(y);
            query[y].push_back(x);
            num[x].push_back(i);
            num[y].push_back(i);
        }
        tarjan(1,0);
        for(int i=0;iprintf("%d\n",ans[i]);
    }
    return 0;
}

hdu 2874
题意:题意和2586差不多。
想法:麻烦点在于数据很大,用vector过不了,可以改用struct。

#include
#include
#include
using namespace std;
const int maxn = 10050;
int N, M, C;
struct node
{
    int id, next, len;
} E[maxn << 1];
int head[maxn << 1], num;
void initlist()
{
    memset(head, -1, sizeof(head));
    num = 0;
}
void adde(int u, int v, int len)
{
    E[num].id = u;
    E[num].len = len;
    E[num].next = head[v];
    head[v] = num++;
}

int fa[maxn<<1];
void initfa()
{
    for(int i = 0; i <= N; i++)
        fa[i] = i;
}
int Find(int id)
{
    if(id == fa[id])
        return id;
    else 
        return fa[id] = Find(fa[id]);
}
void addu(int u, int v)
{
    int x = Find(u);
    int y = Find(v);
    if(x != y)    fa[x] = y;
}

int p[16][maxn], dep[maxn], dis[maxn], vis[maxn];
void DFS(int u, int FA)
{
    vis[u] = 1;
    for(int l = head[u]; l != -1; l = E[l].next)
    {
        int id = E[l].id;
        if(id == FA||vis[id]) continue;
        dep[id] = dep[u] + 1;
        dis[id] = dis[u] + E[l].len;
        p[0][id] = u;
        DFS(id, u);
    }
}
void initlca()
{
    memset(p, -1, sizeof(p));
    memset(dep,0,sizeof(dep));
    memset(dis, 0, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= N; i++)
    {
        if(!vis[i])
            DFS(i, -1);
    }
    for(int k = 0; k + 1 <= 15; k++)
        for(int u = 1; u <= N; u++)
        {
            if(p[k][u] < 0) p[k + 1][u] = -1;
            else
                p[k + 1][u] = p[k][p[k][u]];
        }
}
int LCA(int u, int v)
{
    if(dep[u] > dep[v]) swap(u, v);
    for(int k = 0; k <= 15; k++)
    {
        if((dep[v] - dep[u]) >> k & 1)
            v = p[k][v];
    }
    if(u == v) return u;
    for(int k = 15; k >= 0; k--)
    {
        if(p[k][u] != p[k][v])
        {
            u = p[k][u];
            v = p[k][v];
        }
    }
    return p[0][u];
}

int main ()
{
    while(~scanf("%d%d%d", &N, &M, &C))
    {
        initlist();
        initfa();
        int u, v, ds;
        for(int i = 1; i <= M; i++)
        {
            scanf("%d%d%d", &u, &v, &ds);
            adde(u, v, ds);
            adde(v, u, ds);
            addu(u, v);
        }
        initlca();
        for(int i=1;i<=C;i++)
        {
            scanf("%d%d",&u,&v);
            int x=Find(u);
            int y=Find(v);
            if(x==y)
            {
                printf("%d\n",dis[u]+dis[v]-2*dis[LCA(u,v)]);
            }
            else
                printf("Not connected\n");
        }
    }
    return 0;
}

poj2763
题意:村子里一堆小木屋,麻麻在一个屋里,小盆友在其他屋里喊麻麻带他回家。麻麻想让你帮她算出来她还有多久能接到下一个孩子。
当有0出现时,后面是孩子所在的小屋;当有1出现时 去小屋的时间要因外界原因发生改变,并且还要去到这个小屋。
想法:刚开始想的就是 以麻麻为祖先 她去接下一个孩子的时间=她从起点直接接下一个孩子的时间-她从起点接当前孩子的时间
但是这样的时间必须就是一定了 没办法改变时间 又想到用线段树
鹅鹅鹅鹅鹅鹅套了线段树的板子虽然最后能够编译运行 但是wa了……

去学习别人的方法,发现有两种方法:1.lca+树状数组 2.lca+树链剖分
树链剖分太陌生了,马克一下,先看了树状数组。
每一条边对应着一段时间,运用时间戳(新玩意 查了一下是节点的搜索次序 dfs序)emmmmm先存一下。

ps:先写下来 万一哪天突然让我讲 不会讲就gg了

你可能感兴趣的:(做题报告)