snnu(1110) 传输网络 (并查集+路径压缩+离线操作 || 线段树)

1110: 传输网络

Time Limit: 3 Sec  Memory Limit: 512 MB
Submit: 43  Solved: 18
[Submit][Status][Web Board] [Edit]

Description

Byteland国家的网络单向传输系统可以被看成是以首都Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的。现在他们开始在其他城市陆续建立了新的基站,命令“C x“代表在城市x建立了一个新的基站,不会在同一个城市建立多个基站;城市编号为1到n,其中城市1就是首都Bytetown。在建立基站的过程中他们还会询问某个城市的网络信号是从哪个城市传输过来的,命令”Q x“代表查询城市x的来源城市。

Input

输入有多组测试数据。每组数据的第一行包含两个正整数n和m(1 <= n,m <= 100,000),分别代表城市数和命令数。接下来n-1行,每行两个正整数u和v,代表一条从城市u到城市v的网络传输通道。之后的m行,每行一个命令”C x“或”Q x”。
所有输入的n和m的总和分别都不超过500,000,两组输入数据之间用一个空行隔开。

Output

对于每个查询命令,输出一个整数y,表示来源城市。每两组数据之间用一个空格隔开。

Sample Input

3 4
1 2
2 3
Q 3
C 2
Q 2
Q 3

Sample Output

1
2
2

HINT

 

Source

2015陕西省大学生程序设计竞赛

题目大意:给定一个关系树,有两种操作,一个是询问这个关系树的根节点,一个是将这个关系树分割。

可以使用并查集,由于关系树可能退化成一条链,每次查询时可能会查询整个链,考虑路径压缩,然而后面可能会有c操作,

对于这种问题,一大堆的询问操作和分割操作,相互交叉,类似于之前做过的一道题,求一段区间中相异数之和,全部读入查询操作,

进行编号,然后依次回答以结尾的查询操作,再根据编号回答,离线操作就是映射了一遍,这道题假设读入所有的c操作,建立一棵

终极树,貌似不能回答没有进行c操作之前的查询,不可能吗?

我们可以倒着做,回答最后一个c之后的查询,然后将这棵树还原到c之前的状态,再次回答,以此类推,将树的状态还原到初始状态,

对每一个状态我们都可以进行带有路压缩的查询了,降低了时间复杂度。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 110000
int n,m;
int q[maxn],c[maxn];  //c数组存的是从开始到结束基站的编号
int father[maxn];    //基站之前的信号发射点
int uset[maxn];
int Rank[maxn];
int answer[maxn];   //从末尾到起始的Q的答案
vector < int >  G[maxn];
int cnum;
int MM;
void init()
{
    cnum=0;  MM=0;
    memset(q,0,sizeof(q));
    memset(c,0,sizeof(c));
    memset(father,0,sizeof(father));
    memset(answer,0,sizeof(answer));
    for(int i=0;i<maxn;i++)
        G[i].clear();
}
void MakeSet()
{
    for(int i=1;i<=n;i++)
        {
           uset[i]=i;
           Rank[i]=0;
        }
}
int Find1(int x)
{
    if(x!=uset[x])   Find1(uset[x]);
     return uset[x];
}
void unionSet(int x,int y)
{
    int fx=Find1(x);
    int fy=Find1(y);
    if(fx!=fy)
    {
       if(Rank[fx]>Rank[fy])
        uset[fy]=fx;
       if(Rank[fx]<Rank[fy])
        uset[fx]=fy;
       if(Rank[fx]==Rank[fy])
       {
          uset[fx]=fy;
          Rank[fx]++;
       }
    }
}
int Find2(int x)
{
   if(x!=uset[x])   uset[x]=Find2(uset[x]);
   return uset[x];
}

void  input()
{
    for(int i=1;i<=n-1;i++)
    {
        int fa,son;
        scanf("%d%d",&fa,&son);
        uset[son]=fa;
    }
    for(int i=1;i<=m;i++)
    {
        char s[10]; int num;
        scanf("%s",s);
        if(s[0]=='Q')
        {
            scanf("%d",&num);
            G[cnum].push_back(num);
        }
        if(s[0]=='C')
        {
            scanf("%d",&num);
            G[++cnum].push_back(num);
            c[cnum]=num;
            father[num]=Find1(uset[num]);
            uset[num]=num;   //分割开
        }
    }
     /*cout<<"cnum "<<cnum<<endl;
     for(int i=cnum;i>=0;i--)
     {
         printf("%d   ",G[i].size());
          for(int j=G[i].size()-1;j>=0;j--)
             {
                printf("%d ",G[i][j]);
             }
        printf("
");
     }*/
}

void solve()
{
   // cout<<"cnum "<<cnum<<endl;
    for(int i=cnum;i>=0;i--)
    {
         if(i==0)
         {
             /*for(int j=G[i].size()-1;j>=1;j--)
             {
                printf("%d ",G[i][j]);
             }
             cout<<endl;*/
           //  cout<<"0 "<<G[i].size()<<endl;00
            for(int j=G[i].size()-1;j>=0;j--)
             {
                 int x=G[i][j];
                 answer[MM++]=Find2(x);
               //  cout<<"0 "<<G[i][j]<<" "<<answer[MM-1]<<endl;
             }
         }
         else
         {
            /* for(int j=G[i].size()-1;j>=1;j--)
             {
                printf("%d ",G[i][j]);
             }
             cout<<endl;*/
             for(int j=G[i].size()-1;j>=1;j--)
             {
                 int x=G[i][j];
                 answer[MM++]=Find2(x);
                // cout<<"1 "<<answer[MM-1]<<endl;
             }
           //  cout<<"G[i][0] "<<G[i][0]<<endl;
            // cout<<"father[G[i][0]] "<<father[G[i][0]]<<endl;
             uset[G[i][0]]=father[G[i][0]];
         }
    }
    for(int i=MM-1;i>=0;i--)
       printf("%d
",answer[i]);

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        MakeSet();
        input();
        solve();
    }
    return 0;
}
View Code

 线段树:

可将每个集合染成同样的颜色,利用前序遍历建立时间戳,然后可得每个节点所管辖的节点,由于查询的是节点的最近祖先, 所以不是单纯的染色操作,

可能会有这种情况出现,先染小区间,再染大区间,如果是普通的染色操作,查询就不符合情况。

在pushdown操作中用颜色标记去更新时,如果之前的颜色标记比现在的标记大,那么就不用更新,因为它问的是最近的祖先。

线段树区间染色:

1:查询操作(统计区间内颜色个数)有其特殊性,只要查询区间包含在当前区间内,并且当前区间有颜色,那么查询区间的颜色与当前区间的颜色相同。

(颜色标记以下的节点都是这个颜色)

2:染色操作,对于标记一般情况下考虑两种情况(之前是否有标记),标记是否可以叠加。

3: pushdown操作,标记向下传递时,用父节点的标记值去更新子节点的val值和标记值。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <vector>
#define LL long long
using namespace std;
int answer=1;
//线段树
//区间染色
const int maxN = 110000;
struct node
{
    int lt, rt, val, turn;
}tree[4*maxN];
 
//向下更新
void pushDown(int id)
{
    if (tree[id].turn != 0)
    {
        tree[id<<1].turn  = max( tree[id << 1].val,tree[id].turn );
        tree[id<<1].val = tree[id<<1].turn ;
 
        tree[id<<1|1].turn = max( tree[id << 1 |1].val ,tree[id].turn );
        tree[id << 1 |1].val= tree[id<<1 | 1].turn;
 
        tree[id].turn = 0;
    }
}
 
//向上更新
void pushUp(int id)
{
    if (tree[id<<1].val == tree[id<<1|1].val)
        tree[id].val = tree[id<<1].val;
    else
        tree[id].val = 0;
}
 
//建立线段树
void build(int lt, int rt, int id)
{
    tree[id].lt = lt;
    tree[id].rt = rt;
    tree[id].val = 0;//每段的初值,根据题目要求
    tree[id].turn = 0;
    if (lt == rt)
    {
        tree[id].turn = 1;
        tree[id].val  = 1;
        return;
    }
    int mid = (lt+rt)>>1;
    build(lt, mid, id<<1);
    build(mid+1, rt, id<<1|1);
}
 
//更改每段的值,多用于染色
void change(int lt, int rt, int id, int col)
{
    if (lt <= tree[id].lt && rt >= tree[id].rt)
    {
        tree[id].val = tree[id].turn = max( tree[id].turn ,col );
        return;
    }
    pushDown(id);
    int mid = (tree[id].lt+tree[id].rt)>>1;
    if (lt <= mid)
        change(lt, rt, id<<1, col);
    if (rt > mid)
        change(lt, rt, id<<1|1, col);
     //pushUp(id);
}
/*
void query(int lt, int rt, int id)
{
    if (lt > tree[id].rt || rt < tree[id].lt)
        return;
    if (tree[id].val != 0)
    {
        answer=tree[id].val;
        return;
    }
    query(lt, rt, id<<1);
    query(lt, rt, id<<1|1);
}*/
//查询单点的值
int query(int lt, int rt, int id)
{
    if (lt <= tree[id].lt && rt >= tree[id].rt)
        return tree[id].val;
    pushDown(id);
    int mid = (tree[id].lt + tree[id].rt) >> 1;
    if (rt <= mid)
        return query(lt, rt, id<<1);
    if (lt > mid)
        return query(lt, rt, id<<1|1);
}
 
 
struct NODE
{
    int l,r;
}a[maxN];
typedef vector<int > INT;
vector<INT > Map(maxN);
int number;
void dfs(int fa)
{
    for(int i=0;i<Map[fa].size();i++)
    {
            int son=Map[fa][i];
            a[son].l= ++number;
            dfs(son);
            a[son].r=number;
    }
}
void init()
{
    for(int i=0;i<maxN;i++)
        Map[i].clear();
}
int main()
{
    //freopen("test.txt","r",stdin);
   //freopen("out.txt","w",stdout);
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(int i=1;i<=n-1;i++)
        {
            int from,to;
            scanf("%d%d",&from,&to);
            Map[from].push_back(to);
        }
        number=1;
        a[1].l=1;
        dfs(1);
        a[1].r=number;
 
        build(a[1].l,a[1].r,1);
     //  for(int i=1;i<=9;i++)
       // printf("%d %d
",a[i].l,a[i].r);
        char str[5];   int id;
        while(m--)
        {
            scanf("%s%d",str,&id);
            //printf("%s
",str);
            if(str[0]=='Q')
            {
               // answer=1;
                answer=query(a[id].l,a[id].l,1);
                printf("%d
", answer);
            }
            if(str[0]=='C')
            {
                change(a[id].l,a[id].r,1,id);
            }
        }
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/xianbin7/p/4680634.html